1
0
mirror of https://github.com/eclipse/paho.mqtt.cpp.git synced 2025-05-09 03:11:23 +08:00

Compare commits

...

295 Commits
v1.1 ... master

Author SHA1 Message Date
fpagliughi
165476b1dc Updated the README and CHANGELOG for release v1.5.2. Did some spellchecks. 2025-03-11 12:33:11 -04:00
fpagliughi
59aa9b4625 #505: Example of retrieving MQTT v5 properties in message received callback 2025-03-11 09:22:16 -04:00
fpagliughi
9a93f49d75 #540 Missing default argument in async_client changed constructor breaks code compatibility. Updated 'client' and 'create_options' construcotrs to agree with 'async_client' 2025-03-10 16:00:49 -04:00
fpagliughi
a682a4a8e4 #537 Fixed the Windows DLL build by exporting message::EMPTY_STR and message::EMPTY_BIN 2025-02-13 09:02:20 -05:00
fpagliughi
01a30cfa0e Updated CHANGELOG for v1.5.1 release. 2025-02-09 16:40:59 -05:00
fpagliughi
c27c7c0a86 More fixes for README 2025-02-09 16:28:22 -05:00
fpagliughi
2849338cd5 Fixed up README 2025-02-09 16:11:58 -05:00
fpagliughi
ef04af6b0a #534 Fix seg fault with clang in get_topic() when publishing a message 2025-02-09 16:09:35 -05:00
fpagliughi
d33c759c78 Minor cleanup of CMake files 2025-02-09 16:09:01 -05:00
fpagliughi
c7560cb52e Updated README and CMakeLists.txt for v1.5.1 2025-02-08 17:55:52 -05:00
fpagliughi
629f334053 #535 Fixed last few files that were not properly licenced for EPL v2.0 2025-02-08 17:49:37 -05:00
Frank Pagliughi
e6240c0240
Merge pull request #532 from Roman3349/fix/cmake-install-target
Fix CMake install target lib path
2025-01-29 08:14:43 -06:00
Roman Ondráček
de1793458f
Fix CMake install target lib path
Signed-off-by: Roman Ondráček <mail@romanondracek.cz>
2025-01-28 17:21:52 +01:00
fpagliughi
22f88a3076 Updated CHANGELOG.md 2025-01-07 21:51:50 -05:00
fpagliughi
2f174a2398 Fixed some corner cases for topic_filter::matches() 2025-01-07 21:25:56 -05:00
fpagliughi
0aec355314 Updated local CI sctipt, buildtst.sh. Fixed cppcheck warnings. 2025-01-07 09:18:17 -05:00
fpagliughi
43c1dbc2ba Bumped Paho C submodule to latest master for testing. 2025-01-07 09:14:35 -05:00
fpagliughi
37921d6556 Updated local CI (buildtst.sh) for current compilers and unit tests. 2025-01-05 19:48:57 -05:00
fpagliughi
9d81b72763 Updated version, README, and CHANGELOG for 1.5.0 release. 2025-01-05 18:12:57 -05:00
fpagliughi
c8aabb5d82 #528 Backed out the connect wait to keep testing publish-before-connect 2025-01-05 16:27:25 -05:00
fpagliughi
2b8bfbb94e #528 Wait for connection in the 'async_publish_time' example 2025-01-04 21:41:28 -05:00
fpagliughi
df82ee4378 #410 Added 'shutdown_event' and reworked consumer to prevent propagating exceptions on shutdown. 2025-01-04 19:29:12 -05:00
fpagliughi
1e7d090229 #524 Fixed copy and move operations for 'subscribe_options'. Added unit tests. 2025-01-04 14:56:39 -05:00
fpagliughi
17e4ee14af Added unit tests for 'subscribe_options' 2025-01-04 11:37:56 -05:00
fpagliughi
5e641dd443 #451 Added low keep alive to async_publish_time to test connected/connection_lost callbacks. 2025-01-03 19:47:55 -05:00
fpagliughi
c9e44add9d Bumped Paho C to the latest develop head 2025-01-03 16:50:27 -05:00
fpagliughi
bc7a4e9715 Some minor cleanup of PRs 2025-01-03 16:48:56 -05:00
Frank Pagliughi
461f765430
Merge pull request #519 from sandro97git/set-callback-fix-potential-deadlock
Fix potential deadlock in set_callback
2025-01-03 16:29:27 -05:00
Frank Pagliughi
7b2b3c2415
Merge pull request #518 from ssams/develop
Add function for checking async consumer event queue size
2025-01-03 16:23:36 -05:00
fpagliughi
9bb127986d #523 Additional ways to create and publish messages with v5 properties 2025-01-03 15:31:44 -05:00
fpagliughi
fa88c2f67b Build and documentation for UNIX sockets 2025-01-03 11:31:17 -05:00
fpagliughi
e50e3fde7a #514 properties::get() should be a const member function 2025-01-03 09:54:59 -05:00
fpagliughi
218ccb380e #504 Bumped CMake requirement to v3.13 so that INSTALL(TARGETS) works outside the current directory 2025-01-03 09:47:14 -05:00
fpagliughi
4eebc8db7c Updated README and CHANGELOG 2025-01-03 07:28:17 -05:00
fpagliughi
5629f89059 Updated Paho C dependency to latest develop branch 2025-01-03 06:36:20 -05:00
fpagliughi
cb468ab90e Merge branch 'v1.4.x' into develop 2025-01-03 06:26:39 -05:00
Sandro Scherer
5956f3b112 Fix potential deadlock in set_callback
Signed-off-by: Sandro Scherer <sand.scherer@gmail.com>
2024-10-08 09:51:20 +02:00
Sebastian Sams
0fee432f02 add function for checking async consumer event queue size
Signed-off-by: Sebastian Sams <sebastian.sams@bestsolution.at>
2024-10-07 06:55:04 +00:00
fpagliughi
6131a30530 Added properties indexer w/ example. Now uint8_t prints properly (as unsigned) 2024-07-13 18:19:10 -04:00
fpagliughi
3f2f7c233b thread_queue holding lock when signaling. 2024-07-13 16:18:04 -04:00
fpagliughi
e558946c1d Added consumer_clear() 2024-07-13 15:49:07 -04:00
fpagliughi
ac1b023eab #503 Fixed documentation generation. 2024-07-13 08:43:22 -04:00
fpagliughi
2e68d0faf2 #503 Fixed documentation generation. 2024-07-13 08:42:43 -04:00
fpagliughi
02b589ea98 Queries to check if consumer thread is closed/done. 2024-07-13 07:42:36 -04:00
fpagliughi
3b857a5e0b Fixed up the new closable thread queue. 2024-07-12 08:34:54 -04:00
fpagliughi
1b71a166a9 Made thread queue closable 2024-07-10 13:26:33 -04:00
fpagliughi
c310578ee6 Updated version, README, and CHANGELOG for v1.4.1 release. 2024-07-09 20:15:04 -04:00
fpagliughi
c892569f54 For backward compatibility, the legacy message consumer calls should ignore 'connected' events. 2024-07-08 20:51:17 -04:00
fpagliughi
1c08a3e4a4 Copied certificates from Paho C library for testing SSL/TLS examples, w/ Mosquitto config 2024-07-08 20:51:00 -04:00
fpagliughi
4d51422c8b Updated format script (fmt.sh) to skip externals/ dir 2024-07-07 18:56:41 -04:00
fpagliughi
0b5c312d9f Made 'message_arrived_event' just a message pointer 2024-07-07 10:34:01 -04:00
fpagliughi
604716c2e7 Cleaned up doc comments for the new event consumer methods. 2024-07-07 00:29:53 -04:00
fpagliughi
3e15d873c4 #458 Set disconnected handler for the consumer queue. 2024-07-06 23:30:52 -04:00
fpagliughi
3ef1292f45 Implemented new event consumer examples and fixed some bugs with it. 2024-07-06 20:53:21 -04:00
fpagliughi
0b85abd745 Reverted flat example directory. 2024-07-06 20:00:51 -04:00
fpagliughi
43e8ef67a3 Converted the consumer message queue to an event queue. 2024-07-06 19:42:41 -04:00
fpagliughi
5357a282d7 Fixed connect with listener 2024-07-06 14:33:46 -04:00
fpagliughi
03dfef98ac Encapsulated quit functionality in 'data_publish' example 2024-07-06 10:40:04 -04:00
fpagliughi
0bf6394fb3 Added ^C handling to some examples for clean exits. 2024-07-05 20:59:19 -04:00
fpagliughi
33d219a2a3 Converted 'data_publish' example to use C++17 std::filesystem 2024-07-05 18:58:19 -04:00
fpagliughi
be263432c1 Updated examples to optionally take server URI from command line. 2024-07-05 15:53:12 -04:00
fpagliughi
33d6251995 Added a 'to_string()' and 'operator<<()' for reason codes. 2024-07-05 13:29:14 -04:00
fpagliughi
958a618067 #418 Fixed broken/bad merge 2024-07-04 19:13:52 -04:00
Stan Wijckmans
9e378f5ae0 Call callback immediately when token was already completed. 2024-07-04 17:28:07 -04:00
fpagliughi
28477caf53 Fixed display of 8, 16-bit properties 2024-07-03 17:32:41 -04:00
fpagliughi
134e40db30 Made an explicit 'shared' target in CMake with 'paho-mqttpp3' a default for whichever target was built. 2024-07-03 17:32:00 -04:00
fpagliughi
a4bb565343 Simplified the examples CMake build file 2024-07-03 14:17:47 -04:00
fpagliughi
b8d221f6d7 #355 CMake selecting C++ standard via target options 2024-07-03 13:58:25 -04:00
fpagliughi
c538bdef99 Removed legacy CI files for Travis and AppVeyor 2024-07-03 13:22:49 -04:00
fpagliughi
b5032a8820 Added GitHub CI action 2024-07-03 10:04:02 -04:00
fpagliughi
9be08dd441 Removed deprecation warnings in connect options because they _always_ trigger for internal code. 2024-06-27 08:49:27 -04:00
fpagliughi
91bfc64bdd Fixed doc comment and deprecation warnings. 2024-06-26 21:45:20 -04:00
fpagliughi
381caad141 - Added properties iterator.
- Added property typeid, type name, and cleaned up constructor.
- Exception checks if return code is reason code.
- Added 'server_property_v5' example app.
2024-06-26 18:56:05 -04:00
fpagliughi
e3d6ad0710 Some cleanup of token and properties. Got rid of fake reason code, MQTTPP_V3_CODE 2024-06-24 18:04:48 -04:00
fpagliughi
d5c3487c28 Removed manual implementation of make_unique<>() 2024-06-24 15:10:50 -04:00
fpagliughi
e9fe874c34 Split out server response code into new 'server_response.cpp' and added some doc comments. 2024-06-23 22:11:29 -04:00
fpagliughi
0c80ef44d4 Cleaned up some of the options, with constexpr, etc. 2024-06-23 18:29:49 -04:00
fpagliughi
2ca93a0384 Added create_options assignment operators. 2024-06-22 20:28:20 -04:00
fpagliughi
d386d30be9 Reworked 'create_options' for all params (serverURI, clientId, etc); new persistence_type variant; reqorked async_client constructors 2024-06-22 20:00:06 -04:00
fpagliughi
54e4e5caf3 Cleaned up reformat 2024-06-18 21:17:45 -04:00
fpagliughi
25e6c612e3 Bumped C++ requirement to C++17, and pushed CMake requirement to v3.12 2024-06-18 16:01:55 -04:00
fpagliughi
7d5dcce159 Reformat sources and added .clang-format files. 2024-06-18 16:01:26 -04:00
fpagliughi
f6a3431adb Bumped version to v1.5 prerelease 2024-06-18 14:14:00 -04:00
fpagliughi
a588b29bc4 Client always created with v5 (universal) persistence format 2024-06-18 14:09:38 -04:00
fpagliughi
90eb5275a9 Removed obdolete m4/ directory (autotools) 2024-06-17 17:08:17 -04:00
fpagliughi
3631bce7e0 Updated build instructions in the README 2024-06-17 09:50:51 -04:00
fpagliughi
26f4948325 #483 Unit tests for moving and copying connect_options_builder objects 2024-06-16 18:54:43 -04:00
fpagliughi
98ba95ada5 #473 Getter for the client's cached copy of the connect options. 2024-06-16 16:06:55 -04:00
fpagliughi
523565e3bb nits 2024-06-16 15:41:36 -04:00
fpagliughi
73e301abf2 #389 Added basic multi-threading info to the README 2024-06-16 15:08:28 -04:00
fpagliughi
c15a1e7b31 Spell checking 2024-06-16 14:07:10 -04:00
fpagliughi
cb9da43934 #480 Fixed Paho C version in 'install_paho_mqtt_c.sh' script. 2024-06-16 13:07:26 -04:00
fpagliughi
4d1778b3b4 Updated README and CHANGELOG for v1.4.0 release 2024-06-16 08:09:59 -04:00
fpagliughi
408007cbfa Updated CHANGELOG 2024-06-16 07:55:04 -04:00
fpagliughi
6a54b658d7 Merge branch 'vruge-patch-1' 2024-06-16 06:56:04 -04:00
fpagliughi
8d58cb41f7 Fixed Windows symbol import/export and system lib dependency 2024-06-16 06:55:02 -04:00
fpagliughi
4cd8565995 Sets the project version (CMP0048) policy for bundled builds to get rid of CMake warning 2024-06-16 06:55:02 -04:00
Adam Aposhian
a45f9db800 export dependencies
Signed-off-by: Adam Aposhian <adam.aposhian@fireflyautomatix.com>
2024-06-16 06:54:55 -04:00
fpagliughi
f2d7c16413 Updated README, CHANGELOG, and legacy version info for upcoming v1.4.0 2024-06-16 06:53:47 -04:00
fpagliughi
78b44fbb7e Reworked C library dependency in CMake 2024-06-16 06:53:47 -04:00
fpagliughi
d09d2ef6ca Some CMake reformatting 2024-06-16 06:53:47 -04:00
fpagliughi
8e437596a1 Moved header files to top-level include/ directory. 2024-06-16 06:53:47 -04:00
fpagliughi
8d41d5ab49 Moved externals/ dir to top-level 2024-06-16 06:53:47 -04:00
fpagliughi
0c9379b593 Moved samples to top-level and renamed 'examples' 2024-06-16 06:53:47 -04:00
fpagliughi
8cebac5c44 Some new property unit tests and reformatting 2024-06-16 06:53:46 -04:00
fpagliughi
c3dbf7ef11 #498 Overloaded property constructor to also take a uint32_t (for a non-breaking change). Updated a few v5 examples. 2024-06-16 06:53:46 -04:00
EmixamPP
3440e3123f build(include): add topic_matcher.h 2024-06-16 06:53:46 -04:00
fpagliughi
0959334148 Fixed and optimized 'topic_matcher' 2024-06-16 06:53:46 -04:00
Christian Strahl
76c2491c21 add missing package dependencies 2024-06-16 06:53:46 -04:00
Frank Pagliughi
085608c56a
Merge pull request #476 from strahlc/feature/add_missing_package_dependencies
add missing package dependencies
2024-06-16 06:48:18 -04:00
Frank Pagliughi
d624b6e3ba
Merge branch 'master' into feature/add_missing_package_dependencies 2024-06-16 06:48:11 -04:00
fpagliughi
360774c337 Fixed Windows symbol import/export and system lib dependency 2024-06-15 20:43:27 -04:00
fpagliughi
3d9bd01bda Sets the project version (CMP0048) policy for bundled builds to get rid of CMake warning 2024-06-15 19:18:21 -04:00
Frank Pagliughi
2d329af56e
Merge pull request #485 from fireflyautomatix/export-dependencies
export dependencies
2024-06-15 17:33:40 -04:00
fpagliughi
a84dbe296a Updated README, CHANGELOG, and legacy version info for upcoming v1.4.0 2024-06-15 17:02:45 -04:00
fpagliughi
1f55b7ed03 Reworked C library dependency in CMake 2024-06-15 16:34:37 -04:00
fpagliughi
b98f10949d Some CMake reformatting 2024-06-15 14:59:25 -04:00
fpagliughi
01b5d0414e Moved header files to top-level include/ directory. 2024-06-15 14:42:22 -04:00
fpagliughi
c3e6ebc341 Moved externals/ dir to top-level 2024-06-15 10:40:28 -04:00
fpagliughi
c36d123042 Moved samples to top-level and renamed 'examples' 2024-06-15 10:05:17 -04:00
fpagliughi
773f4e9d66 Some new property unit tests and reformatting 2024-06-15 09:20:59 -04:00
fpagliughi
584e3bd025 #498 Overloaded property constructor to also take a uint32_t (for a non-breaking change). Updated a few v5 examples. 2024-06-13 21:27:19 -04:00
Frank Pagliughi
f6f0502658
Merge pull request #491 from EmixamPP/master
build(include): add topic_matcher.h
2024-06-13 17:40:38 -04:00
fpagliughi
9fdfe32116 Fixed and optimized 'topic_matcher' 2024-06-02 11:31:37 -04:00
EmixamPP
a13a74f6d2
build(include): add topic_matcher.h 2024-04-09 17:02:16 +02:00
Vitalij
26f4fad7d5 docs: update link for CIPHER LIST FORMAT 2024-03-26 10:31:11 +01:00
Adam Aposhian
07a188b530 export dependencies
Signed-off-by: Adam Aposhian <adam.aposhian@fireflyautomatix.com>
2024-02-16 06:43:33 -07:00
Frank Pagliughi
922e866f4f
Merge pull request #484 from fireflyautomatix/add-token-get-message
add token::get_message
2024-02-15 19:02:06 -05:00
Adam Aposhian
6abf27d977 rename to get_error_message
Signed-off-by: Adam Aposhian <adam.aposhian@fireflyautomatix.com>
2024-02-15 15:36:47 -07:00
Adam Aposhian
40f29b5c62 add token::get_message
Signed-off-by: Adam Aposhian <adam.aposhian@fireflyautomatix.com>
2024-02-13 17:14:08 -07:00
fpagliughi
2bb54f0e4a Added missing Paho legal docs. 2024-02-04 16:52:31 -05:00
Christian Strahl
7da9d6d4b2 add missing package dependencies 2024-01-23 17:55:42 +01:00
fpagliughi
1980bd3e17 Added cbegin() and cend() to string_collection, and fixed docs for the class. 2023-12-09 17:50:26 -05:00
Frank Pagliughi
a513e06d5b
Merge pull request #466 from fireflyautomatix/iterable-string-collection
Iterable string collection
2023-12-09 16:06:39 -05:00
Adam Apsohian
967ce5d7a1 add string_collection iterator 2023-12-08 11:20:11 -07:00
fpagliughi
f7b648a3ce Updated CHANGELOG for v1.3.2 2023-12-05 23:10:02 -05:00
fpagliughi
da7c7f54fd #378 Bad LWT message in async_publish.cpp sample. 2023-12-05 22:23:21 -05:00
fpagliughi
3744db4eef #463 Fixed generator expression for older CMake 2023-12-05 22:22:50 -05:00
fpagliughi
a90ecd555a #416 Removed FindPahoMqttC.cmake. Using Paho C package directly. 2023-11-26 22:25:15 -05:00
fpagliughi
4691652479 Bumped verion strings to v1.3.1 2023-11-23 10:32:50 -05:00
fpagliughi
4ef3cdb165 Updated README 2023-11-22 12:54:04 -05:00
fpagliughi
00311adaf5 Updated README 2023-11-22 12:14:18 -05:00
fpagliughi
bdd8df3d99 Updated README 2023-11-22 12:11:57 -05:00
fpagliughi
4e92ae4bff Started updating README and CHANGELOG for release. 2023-11-22 11:15:27 -05:00
fpagliughi
9635a5157c Fixed segfult in new unit test on Mac 2023-11-20 21:26:15 -05:00
fpagliughi
cd6dd125a7 #343 async_client::try_consume_message_until taking single parameter fails to compile 2023-11-20 20:17:25 -05:00
fpagliughi
02cf646d84 Added Windows DLL export nonsense to make class static constants visible to apps 2023-11-20 19:30:50 -05:00
fpagliughi
6f79d51d53 Fixed build warning with Windows MSVC 2023-11-20 19:28:07 -05:00
fpagliughi
5143d89d92 Got CHANGELOG up to date 2023-11-18 14:32:03 -05:00
fpagliughi
97e94053ae #361 Added missing LICENSE file. 2023-11-18 14:03:25 -05:00
fpagliughi
a7e9b12042 #461 Updated license to EPL-v2.0 2023-11-18 14:01:59 -05:00
fpagliughi
39bfbe1bc6 Added some doc comments. 2023-11-18 12:59:27 -05:00
fpagliughi
c502f84ecf Continued fixing connect and disconnect copy and move functions with new unit tests. See #445 2023-11-17 09:36:57 -05:00
fpagliughi
5ff5484559 #426 Update properties when moving/copying connect options. 2023-11-16 08:59:12 -05:00
Frank Pagliughi
147554f18d
Merge pull request #381 from oledahle/develop
Update documentation on get_mqtt_version()
2023-11-16 08:38:50 -05:00
fpagliughi
144132533e #325 Cache connect options in client to keep memory valid for callbacks like SSL on_error() 2023-11-15 18:29:20 -05:00
fpagliughi
e9db86fa79 #460 Add missing sample 'async_consume_v5.cpp' 2023-11-15 15:33:23 -05:00
fpagliughi
9691e10da3 Updated README and CHANGELOG 2023-11-15 13:15:25 -05:00
fpagliughi
93a8ba26ad #304 Missing create_options::DFLT_C_STRUCT symbol when linking with MSVC. 2023-11-15 10:24:23 -05:00
fpagliughi
8da572133c Updated CMake for new v5 consumer sample app 2023-11-15 10:23:34 -05:00
fpagliughi
550f48f71a Changed the samples to use new 'mqtt://' URL schema 2023-11-13 08:22:20 -05:00
fpagliughi
ba66f28c43 #458 Added consumer notification when broker cleanly disconnects 2023-11-12 20:42:42 -05:00
fpagliughi
b8aa0604ff #429 Remove declaration of connect_options::to_string() with missing implementation. 2023-11-12 20:12:56 -05:00
fpagliughi
94efd557b4 #411 Missing virtual keyword for some client methods 2023-11-12 20:04:14 -05:00
fpagliughi
bb66355be6 #444 Unit tests to check that connect options builder sets properties. 2023-11-12 19:32:47 -05:00
fpagliughi
b94cb27722 Connect option initializers for v5 and WebSockets. 2023-11-12 18:30:41 -05:00
fpagliughi
207241ba46 Updated README and CHANGELOG for upcoming v1.3.0 release 2023-11-12 18:30:03 -05:00
fpagliughi
226ca35f5d Fixed unit test CMake for Catch2 v2.x and added missing catch2_version.h 2023-11-11 16:24:20 -05:00
fpagliughi
f45bc093e5 #313 Fixed persistence unit tests on Windows w/ MSVC 2023-11-11 16:15:04 -05:00
fpagliughi
509da56afc #313 Unit tests building on Windows. Support for Catch2 v3.x. Needed to get rid of make_unique<> for Windows 2023-11-11 16:14:20 -05:00
fpagliughi
6020d661b5 #397 Doc about clean session in connect_options.h is wrong 2023-11-11 10:22:26 -05:00
fpagliughi
ab98b289b6 #442 g++ complains with multiple definition of static constexpr for mixed C++11/17 builds 2023-11-11 09:47:44 -05:00
fpagliughi
f90f3069f1 Fixed static build on *nix systems 2023-11-10 20:00:32 -05:00
fpagliughi
214b66d74c Updated Paho C to v1.3.13 2023-11-10 14:38:43 -05:00
fpagliughi
c7f6d08c4b Updated README with v.13 release plans 2023-11-10 07:42:23 -05:00
fpagliughi
ef021805c7 Updated Paho C submodule to v1.3.12 2023-07-31 11:04:07 -04:00
fpagliughi
e631445088 Minor cleanup of CMake files 2023-07-31 11:01:09 -04:00
Frank Pagliughi
d43808e7c9
Merge pull request #374 from HpLightcorner/mqtt-c-submodule
Add paho.mqtt.c as a submodule
2023-07-29 16:38:56 -04:00
Frank Pagliughi
7cfcd4bf87
Merge branch 'develop' into mqtt-c-submodule 2023-07-29 16:32:27 -04:00
Frank Pagliughi
62868ec250
Merge pull request #350 from koalo/master
avoid adding Paho MQTT C library twice
2023-07-29 16:31:01 -04:00
Frank Pagliughi
95928b60ad
Merge pull request #445 from ssams/develop
Fix copy/move constructor for connect/disconnect opts with properties
2023-07-29 15:50:06 -04:00
Sebastian Sams
5d267a0fff fix copy/move ctor for disconnect opts with props
Signed-off-by: Sebastian Sams <sebastian.sams@bestsolution.at>
2023-07-18 12:07:41 +00:00
Sebastian Sams
8c60105e06 fix copy/move ctor for connect opts with props
ensures pointers to the properties are updated properly
in move and copy constructors, so the c struct pointers remain valid

Signed-off-by: Sebastian Sams <sebastian.sams@bestsolution.at>
2023-07-18 12:20:09 +02:00
fpagliughi
20f09cc4ec Merge branch 'master' into develop 2023-07-13 22:24:43 -04:00
Frank Pagliughi
6bfe50769b
Merge pull request #407 from dberlin/patch-1
Fix nodiscard warnings in sync client
2023-06-26 14:34:46 -04:00
fpagliughi
44d186ecb1 #425 Enable build warnings, and fix the existing ones. 2023-06-26 14:27:24 -04:00
Frank Pagliughi
121cf9fd6e
Merge pull request #440 from DevnathNair/fix-codebase-typos
Fix Typos Across The Project
2023-06-22 13:34:24 -04:00
Devnath Nair
c6947e1e61 Fix typos across the project
Signed-off-by: Devnath Nair <mail@devnathnair.com>
2023-06-09 23:14:29 +10:00
fpagliughi
d1a294393c #428 Fixed type in create_options::set_mqtt_version() 2023-03-15 22:09:02 -04:00
fpagliughi
894b168fb4 Updating create and connect options to better deal with MQTT protocol version 2023-03-15 22:04:14 -04:00
fpagliughi
5ab7374445 Defaulting connect version to v5 if specified in create options. 2023-01-30 17:30:35 -05:00
fpagliughi
d4106b8b49 Merge branch 'develop' of github.com:eclipse/paho.mqtt.cpp into develop 2023-01-30 17:27:10 -05:00
Daniel Berlin
ec01ed39a7 Fix nodiscard warnings in sync client
In later C++ versions, std::async is marked nodiscard. See https://cplusplus.github.io/LWG/issue2856.
The code is correct in that it is properly blocking for the result, but it will now warn since this is considered "not the way".
Adding .wait() to the end of the calls, which explicitly waits on the future, should be the correct fix.

Signed-off-by: Daniel Berlin <dberlin@dberlin.org>
2022-08-30 11:39:48 -04:00
Heinz-Peter Liechtenecker
53367104cb Append static suffix on all platforms 2022-08-16 13:30:55 +02:00
fpagliughi
67b1e5e40d #385 Thread queue deadlock with multiple consumers 2022-04-28 23:24:06 -04:00
fpagliughi
37d7616816 Updated README 2022-04-28 17:36:29 -04:00
fpagliughi
a405038c0d Updated README with new features. 2022-04-27 11:15:03 -04:00
fpagliughi
dda7445d1e Added initial implementation of a 'topic_matcher' class. 2022-04-27 11:09:18 -04:00
fpagliughi
fb740d8fd8 Added 'topic_filter' class. 2022-04-23 17:47:21 -04:00
Ole Henrik Dahle
5e012176f3 Updated documentation on get_mqtt_version().
Signed-off-by: Ole Henrik Dahle <ole.henrik.dahle@indra.no>
2022-03-08 13:45:51 +01:00
Heinz-Peter Liechtenecker
f6637d754c Adding an option to build paho.mqtt.c from submodule
Signed-off-by: Heinz-Peter Liechtenecker <heinz.liechtenecker@gmail.com>
2022-01-11 12:32:38 +01:00
Heinz-Peter Liechtenecker
7af2b3f6f7 Addding paho.mqtt.c as a GIT submodule
Signed-off-by: Heinz-Peter Liechtenecker <heinz.liechtenecker@gmail.com>
2022-01-11 12:27:59 +01:00
Florian Kauer
f2a15f4abf avoid adding Paho MQTT C library twice
Signed-off-by: Florian Kauer <koalo@koalo.de>
2021-07-19 10:19:33 +02:00
fpagliughi
2ff3d155dc #253 implicit capture of 'this' via '[=]' is deprecated in C++20 2021-04-30 11:42:38 -04:00
fpagliughi
e724de5b7c Updated readme with some new features. 2021-04-30 10:42:49 -04:00
fpagliughi
48787dc039 #337 copy/move of caPath_ in ssl_options 2021-04-30 10:35:04 -04:00
fpagliughi
deafcaf036 Merge branch 'develop' of github.com:eclipse/paho.mqtt.cpp into develop 2021-04-30 08:41:45 -04:00
fpagliughi
611969a003 Merge branch 'master' into develop 2021-04-29 21:42:44 -04:00
fpagliughi
a04195a0b9 Merge branch 'master' into develop 2021-04-26 19:37:12 -04:00
Frank Pagliughi
4622b98f5e
Merge pull request #330 from doleron/add_build_gitignore
added /build/ folder to .gitignore
2021-04-26 19:36:55 -04:00
doleron
e3b0ced4db cosmetics 2021-04-03 22:56:08 -03:00
doleron
5fdd9a2d45 added build folder to gitignore 2021-04-03 22:39:53 -03:00
fpagliughi
09336e127d #317 String constructor using just len instead of end iterator. 2021-01-31 09:48:16 -05:00
fpagliughi
4c1f545b5c Added Session Expiry Interval to v5 chat sample to test #323 2021-01-31 09:16:56 -05:00
fpagliughi
990bea092a Removed friend access for test objects that no longer exist. 2021-01-31 09:16:35 -05:00
fpagliughi
33921c8b68 Minor update to README for v1.2.0 release 2020-12-27 14:42:38 -05:00
fpagliughi
a707392fac Minor update to README for v1.2.0 release 2020-12-27 14:40:11 -05:00
fpagliughi
c338346524 Added more message and properties unit tests, with some fixes for moves 2020-12-27 14:01:13 -05:00
fpagliughi
c5b0c52f91 Messages properly copying/moving v5 properties in copy and move constructors and assignments. 2020-12-27 12:15:17 -05:00
fpagliughi
52891c2297 Doxygen comments for new will_options parameters. 2020-12-27 11:50:40 -05:00
fpagliughi
3b400b5942 #266 Copy/move properties in will_options copy and move constructors and assignment operators. Also added parameter for properties in all the will_options constructors. 2020-12-27 11:47:01 -05:00
fpagliughi
84b060eb8a Updated the README and CHANGELOG for the v1.2.0 release. 2020-12-27 10:29:44 -05:00
fpagliughi
1bf762ff5c Bumped the version to 1.2.0 2020-12-27 00:17:06 -05:00
fpagliughi
e2f318836f Updated Doxygen comments 2020-12-26 23:41:48 -05:00
fpagliughi
84a52d8157 Updated readme with new feature 2020-12-26 17:36:58 -05:00
fpagliughi
526f8aaaaf #311 Added ALPN protocols to the SSL options 2020-12-26 17:33:46 -05:00
fpagliughi
4c62de40b3 Updated the README with new bug fix 2020-12-26 12:54:23 -05:00
fpagliughi
c1f48fd7de #300 Fixed reconnect. Added 'timeout_error' exception. Sync client properly throwing timeout errors. Added 'sync_reconnect' sample app. 2020-12-26 12:40:53 -05:00
fpagliughi
26878bfb64 Updates to the readme 2020-12-23 11:24:22 -05:00
fpagliughi
19e314d81a Added ability to specify properties for subscribe and unsubscribe packets. Added the sync_consume_v5 sample to show use of subscribe identifiers. 2020-12-22 18:03:45 -05:00
fpagliughi
db1b5d1131 Minor update to README 2020-12-22 10:37:54 -05:00
fpagliughi
cc8dbd36f2 Restored async_publish client ID (required for persistence) 2020-12-21 21:38:46 -05:00
fpagliughi
b9d5ab00b6 Fixed windows build and warnings 2020-12-21 21:30:12 -05:00
fpagliughi
be69d681fc Updated the sample apps 2020-12-21 18:04:40 -05:00
fpagliughi
6e01c7ae90 Updated the file-persistence example in the data_publish sample. 2020-12-21 16:44:43 -05:00
fpagliughi
6e1444155c Removed persistence encoding, reworked persistence internals, and created a file persistence example. 2020-12-21 02:12:35 -05:00
fpagliughi
c955131ee7 Updated the persistence encoding/decoding sample app. 2020-12-20 21:56:20 -05:00
fpagliughi
a36a75abfe Reworked the persistence encoding interface 2020-12-20 16:04:33 -05:00
fpagliughi
6f2a32120a #293 Added a multi-threaded pub/sub sample. 2020-12-20 13:54:12 -05:00
fpagliughi
96b7126100 Updated sync_consume sample to update connection data. 2020-12-19 16:44:28 -05:00
fpagliughi
7b64cb6503 Restored sync_consume with auto reconnect. 2020-12-15 11:17:58 -05:00
fpagliughi
bc17546895 Fixes for connection update callback and connect_data 2020-12-15 11:16:43 -05:00
fpagliughi
25138e9091 Added 'update connection' callback support and websocket sample. 2020-12-14 17:46:06 -05:00
fpagliughi
b9e92b2b93 Minor comments in async_publish_time sample. 2020-12-13 10:56:33 -05:00
fpagliughi
6e87712bf4 #275 Keep -static suffix for Paho C on Windows 2020-12-13 10:51:53 -05:00
fpagliughi
b7b497d983 Fixed a new double-free error with user-defined persistence. 2020-12-07 22:48:49 -05:00
fpagliughi
a2e6645533 Cleaned up some of the sample apps. Rejiggered create_options contructors, breaking previous commits 2020-12-07 18:30:23 -05:00
Frank Pagliughi
bafe6b4100
Merge pull request #282 from klaussnd/debian-package
Add possibility to create a Debian/Ubuntu package
2020-12-07 15:54:24 -05:00
fpagliughi
b3992ecaaf Minor update to README to list some new features. 2020-12-07 15:50:40 -05:00
fpagliughi
9b9f49ed52 Updated README for upcoming C v1.3.8 release. 2020-12-07 10:04:26 -05:00
fpagliughi
2237a03fee Added SSL callback to sample app 2020-12-06 16:32:43 -05:00
fpagliughi
f57c54f8d4 Added SSL option callbacks 2020-12-06 14:32:22 -05:00
fpagliughi
9321cc5e6a Bumped Travis CI to use C v1.3.7 2020-12-06 10:05:37 -05:00
fpagliughi
22a85aa7c5 Added support for persistence encoding/decoding. 2020-12-06 10:04:36 -05:00
fpagliughi
d67a92dbce Updated CONTRIBUTING doc and README 2020-12-05 10:49:57 -05:00
fpagliughi
a09b168872 Added HTTP/S proxy settings to connect options builder. Fixed some Windows build warnings. 2020-12-05 09:57:54 -05:00
fpagliughi
3a46e4fd6c Added http/https proxy options 2020-11-17 09:12:51 -05:00
fpagliughi
076fc9977d #301 Fixed missing create_options.h on install 2020-10-29 16:45:43 -04:00
fpagliughi
e46317ac15 Added HTTP header to connect options 2020-10-26 22:35:11 -04:00
fpagliughi
c8a8dba470 Finished implementation of name_value_collection 2020-10-26 20:15:36 -04:00
fpagliughi
d04883c1db Finished implementing persistence Catch2 unit test conversion 2020-10-26 11:16:18 -04:00
fpagliughi
27615994cf Minor update to readme. 2020-10-26 10:38:35 -04:00
fpagliughi
2ffc120690 Restored non-blank client ID for the async unit tests. 2020-10-25 23:14:37 -04:00
fpagliughi
5b77b0e427 Moved unit test persistence directory to relative location 2020-10-25 11:08:26 -04:00
fpagliughi
77a0455ebb Updated the CI to remove the CppUnit tests 2020-10-25 01:28:18 -04:00
fpagliughi
b20494c10a Moved async_client unit tests to Catch2 2020-10-25 01:21:40 -04:00
fpagliughi
9adf62f544 Moved client unit test to Catch2 2020-10-24 23:22:52 -04:00
fpagliughi
3cee5d5d01 Moved client persistence unit tests to Catch2 2020-10-24 22:12:10 -04:00
fpagliughi
749c454397 Cleaned up the mock action listener a little. 2020-10-24 16:03:03 -04:00
fpagliughi
035be0b867 Added forgotten mock_action_listener 2020-10-24 15:46:10 -04:00
fpagliughi
60a6e30add Moved token unit tests to Catch2 2020-10-24 13:23:02 -04:00
fpagliughi
f5e0d7f1cf Moved delivery_response_options unit tests to Catch2. Merged with response_options 2020-10-24 12:09:27 -04:00
fpagliughi
095b4e166b Moved response_options unit tests to Catch2 2020-10-24 11:42:43 -04:00
fpagliughi
6bcc6f80a9 Moved disconnect_options unit tests to Catch2 2020-10-24 11:19:11 -04:00
fpagliughi
4a5de0db4f Moved ssl_options unit tests to Catch2 2020-10-24 00:42:19 -04:00
fpagliughi
98da500fb5 Moved exception unit tests to Catch2 2020-10-23 12:49:32 -04:00
fpagliughi
7f79287e7c Restored creation (via create_options) to default to MQTT v5 2020-10-23 12:26:55 -04:00
fpagliughi
0893505ff9 Updated the Paho C build script to v1.3.6 2020-10-23 09:29:20 -04:00
fpagliughi
2189328e06 Added disconnect options builder and message builder 2020-10-22 23:47:17 -04:00
fpagliughi
83724d2f0f Added SSL options builder 2020-10-22 21:18:54 -04:00
fpagliughi
f90a968d58 Added connect_options_builder. 2020-10-18 10:47:06 -04:00
fpagliughi
5db5b038a0 Implemented create_options and put them into the 'async_publish_time' sample app 2020-10-18 09:47:53 -04:00
fpagliughi
9bc5403b62 Converted connect_options unit tests from CppUnit to Catch2 2020-08-24 00:27:56 -04:00
fpagliughi
51da7fb8f3 Converted will_options unit tests from CppUnit to Catch2 2020-08-23 23:36:02 -04:00
fpagliughi
8602224a64 Converted buffer_ref unit tests from CppUnit to Catch2 2020-08-23 22:21:29 -04:00
fpagliughi
5224a78d3c Converted message unit test from CppUnit to Catch2 2020-08-23 20:28:35 -04:00
fpagliughi
75e4701a10 Rewrote topic unit tests for Catch2 2020-08-23 17:51:10 -04:00
fpagliughi
e337b3e173 Started a name/value collection. Moved string collection unit test from CppUnit to Catch2 2020-08-23 16:54:07 -04:00
Klaus Schneider-Zapp
b7e4e4322d Update README to describe building a Debian package
Signed-off-by: Klaus Schneider-Zapp <klaus_snd@web.de>
2020-08-06 14:35:19 +02:00
Klaus Schneider-Zapp
82d8503a37 Add possibility to build a debian package
Signed-off-by: Klaus Schneider-Zapp <klaus_snd@web.de>
2020-08-06 14:35:15 +02:00
Frank Pagliughi
f8c1270ad1
Merge pull request #230 from klaussnd/fix-cmake
Find required dependency Threads
2020-03-29 12:09:40 -04:00
fpagliughi
bd4a4dc705 Issues #224 and #255. Fixed when subscribing to MQTT v3 broker with array of one topic causes segfault. 2020-03-29 11:59:35 -04:00
fpagliughi
f3a2a52259 #211, #223, #235 Removed calls to Log() in Paho C lib.
#231 Added support for disconnected callback.
2020-01-13 11:07:19 -05:00
Klaus Schneider-Zapp
29055d7185 Find required dependency Threads
Signed-off-by: Klaus Schneider-Zapp <klaus_snd@web.de>
2019-12-07 13:21:27 +01:00
fpagliughi
3490d3950f #227 Deadlock in thread_queue.h 2019-11-20 22:48:21 -05:00
198 changed files with 26977 additions and 18109 deletions

273
.clang-format Normal file
View File

@ -0,0 +1,273 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -4
#AlignAfterOpenBracket: Align
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
# Clang-17
#AlignConsecutiveShortCaseStatements:
# Enabled: false
# AcrossEmptyLines: false
# AcrossComments: false
# AlignCaseColons: false
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterAttributes: Never
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Custom
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 94
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: BeforeHash
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: true
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
KeepEmptyLinesAtEOF: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: NextLine
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
PPIndentWidth: -1
QualifierAlignment: Leave
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: pb
BasedOnStyle: google
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
# Clang Format >v17
#SpacesInParens: Never
#SpacesInParensOptions:
# InCStyleCasts: false
# InConditionalStatements: false
# InEmptyParentheses: false
# Other: false
SpacesInSquareBrackets: false
Standard: Auto
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...

View File

@ -5,7 +5,7 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = tab indent_style = space
indent_size = 4 indent_size = 4
end_of_line = lf end_of_line = lf
insert_final_newline = true insert_final_newline = true

View File

@ -0,0 +1,134 @@
# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform.
# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml
name: CMake on multiple platforms
on:
push:
branches: [ "master", "develop" ]
pull_request:
branches: [ "master", "develop" ]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
# Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.
fail-fast: false
# Set up a matrix to run the following 3 configurations:
# 1. <Windows, Release, latest MSVC compiler toolchain on the default
# runner image, default generator>
# 2. <Linux, Release, latest GCC compiler toolchain on the default
# runner image, default generator>
# 3. <Linux, Release, latest Clang compiler toolchain on the default
# runner image, default generator>
#
# To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list.
matrix:
os: [ubuntu-latest, windows-latest]
build_type: [Release, Debug]
c_compiler: [gcc, clang, cl]
include:
- os: windows-latest
c_compiler: cl
cpp_compiler: cl
- os: ubuntu-latest
c_compiler: gcc
cpp_compiler: g++
- os: ubuntu-latest
c_compiler: clang
cpp_compiler: clang++
exclude:
- os: windows-latest
c_compiler: gcc
- os: windows-latest
c_compiler: clang
- os: ubuntu-latest
c_compiler: cl
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Mosquitto (ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa
sudo apt-get update
sudo apt-get -y install mosquitto
# TODO: Figure out how to install and run mosquitto on Windows for unit tests.
# - name: Install Mosquitto from Source (windows)
# if: matrix.os == 'windows-latest'
# run: |
# git clone https://github.com/eclipse/mosquitto.git
# cd mosquitto
# git checkout "v2.0.18"
# mkdir build && cd build
# cmake .. -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
# cmake --build . --target install
# "C:\Program Files\mosquitto\mosquitto install"
- name: Install Catch2 v2 from Source (ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
git clone https://github.com/catchorg/Catch2.git
cd Catch2
git checkout "v2.13.8"
mkdir build && cd build
cmake .. -DBUILD_TESTING=OFF -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
sudo cmake --build . --target install
- name: Install Catch2 v2 from Source (windows)
# For Windows, it's important to build Catch2 with the same 'build_type'
# as the Paho C++ library, so that they link without errors.
if: matrix.os == 'windows-latest'
run: |
git clone https://github.com/catchorg/Catch2.git
cd Catch2
git checkout "v2.13.8"
mkdir build && cd build
cmake .. -DBUILD_TESTING=OFF -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
cmake --build . --config ${{ matrix.build_type }} --target install
- name: Set reusable strings
# Turn repeated input strings (such as the build output directory) into step outputs.
# These step outputs can be used throughout the workflow file.
id: strings
shell: bash
run: |
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
- name: Configure CMake
# We configure to build the examples, unit tests, and Paho C library.
#
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required
# if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: >
cmake -B ${{ steps.strings.outputs.build-output-dir }}
-DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
-DCMAKE_C_COMPILER=${{ matrix.c_compiler }}
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
-DPAHO_BUILD_EXAMPLES=ON
-DPAHO_BUILD_TESTS=ON
-DPAHO_WITH_MQTT_C=ON
-S ${{ github.workspace }}
- name: Build
# Build your program with the given configuration. Note that --config is
# needed for Windows because the default Windows generator is a multi-config
# generator (Visual Studio generator).
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }}
- name: Test
if: matrix.os == 'ubuntu-latest'
working-directory: ${{ steps.strings.outputs.build-output-dir }}
# Execute tests defined by the CMake configuration.
# Note that --build-config is needed because the default Windows generator
# is a multi-config generator (Visual Studio generator).
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ctest --build-config ${{ matrix.build_type }}

3
.gitignore vendored
View File

@ -7,6 +7,8 @@ obj/
.deps/ .deps/
.libs/ .libs/
*.vp* *.vp*
*.vtg
*~
aclocal.m4 aclocal.m4
# AC_CONFIG_AUX_DIR created by /bootstrap # AC_CONFIG_AUX_DIR created by /bootstrap
autotools_build/ autotools_build/
@ -19,3 +21,4 @@ Makefile.in
*.lo *.lo
*.o *.o
*.stamp *.stamp
/build/

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "src/externals/paho-mqtt-c"]
path = externals/paho-mqtt-c
url = https://github.com/eclipse/paho.mqtt.c.git

View File

@ -1,175 +0,0 @@
language: cpp
sudo: required
dist: xenial
os: linux
before_install:
- ./install_catch2.sh
- ./travis_install.sh
install:
# Install Paho MQTT C (Need only paho-mqtt3a and paho-mqtt3as)
- ./install_paho_mqtt_c.sh
addons:
apt:
sources:
- sourceline: 'ppa:mosquitto-dev/mosquitto-ppa'
packages:
- cppcheck
- libcppunit-dev
- git
- cmake
- cmake-data
- doxygen
- mosquitto
matrix:
include:
# GCC 5
- compiler: gcc
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mosquitto-dev/mosquitto-ppa'
packages:
- g++-5
- cppcheck
- libcppunit-dev
- git
- cmake
- cmake-data
- doxygen
- mosquitto
env: COMPILER=g++-5
# GCC 6
- compiler: gcc
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mosquitto-dev/mosquitto-ppa'
packages:
- g++-6
- cppcheck
- libcppunit-dev
- git
- cmake
- cmake-data
- doxygen
- mosquitto
env: COMPILER=g++-6
# GCC 7
- compiler: gcc
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mosquitto-dev/mosquitto-ppa'
packages:
- g++-7
- cppcheck
- libcppunit-dev
- git
- cmake
- cmake-data
- doxygen
- mosquitto
env: COMPILER=g++-7
# GCC 8
- compiler: gcc
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mosquitto-dev/mosquitto-ppa'
packages:
- g++-8
- cppcheck
- libcppunit-dev
- git
- cmake
- cmake-data
- doxygen
- mosquitto
env: COMPILER=g++-8
# Clang 3.9
- compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mosquitto-dev/mosquitto-ppa'
packages:
- clang-3.9
- cppcheck
- libcppunit-dev
- git
- cmake
- cmake-data
- doxygen
- mosquitto
env: COMPILER=clang++-3.9
# Clang 4.0
- compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: 'ppa:mosquitto-dev/mosquitto-ppa'
packages:
- clang-4.0
- cppcheck
- libcppunit-dev
- git
- cmake
- cmake-data
- doxygen
- mosquitto
env: COMPILER=clang++-4.0
# Clang 7
- compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-xenial-7
- sourceline: 'ppa:mosquitto-dev/mosquitto-ppa'
packages:
- clang-7
- cppcheck
- libcppunit-dev
- git
- cmake
- cmake-data
- doxygen
- mosquitto
env: COMPILER=clang++-7
# Clang 8
- compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-xenial-8
- sourceline: 'ppa:mosquitto-dev/mosquitto-ppa'
packages:
- clang-8
- cppcheck
- libcppunit-dev
- git
- cmake
- cmake-data
- doxygen
- mosquitto
env: COMPILER=clang++-8
exclude:
- compiler: gcc
script:
# Test build
- ./travis_build.sh
# Static Analysis
- cppcheck --enable=all --std=c++11 --force --quiet src/*.cpp

View File

@ -1,4 +1,188 @@
# Change Log # Change Log
#
# Eclipse Paho MQTT C++ Library
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Version 1.5.2](https://github.com/eclipse/paho.mqtt.cpp/compare/v1.5.1..v1.5.2) (2025-03-11)
- Fixed the Version number and string.
- Synchronous `Client` constructors updated to use `persistence_type` and (just) `create_options`
- Restored compatibility with `async_client`
- [#505](https://github.com/eclipse-paho/paho.mqtt.cpp/issues/505): Example of retrieving MQTT v5 properties in message received callback
- [#537](https://github.com/eclipse-paho/paho.mqtt.cpp/issues/537) Fixed the Windows DLL build by exporting message::EMPTY_STR and message::EMPTY_BIN
- [#540](https://github.com/eclipse-paho/paho.mqtt.cpp/issues/537) Missing default argument in `async_client` changed constructor breaks code compatibility
## [Version 1.5.1](https://github.com/eclipse/paho.mqtt.cpp/compare/v1.5.0..v1.5.1) - (2025-02-09)
- Minor fixes to README and docs
- [#532](https://github.com/eclipse-paho/paho.mqtt.cpp/pull/532) Fix CMake install target lib path
- [#534](https://github.com/eclipse-paho/paho.mqtt.cpp/issues/534) Fixed seg fault with clang in get_topic() when publishing a message
- [#535](https://github.com/eclipse-paho/paho.mqtt.cpp/issues/535) Fixed last few files that were not properly licenced for EPL v2.0
## [Version 1.5.0](https://github.com/eclipse/paho.mqtt.cpp/compare/v1.4.1..v1.5.0) - (2025-01-07)
- Code base updated to to C++17
- Now a C++17 compiler is required to compile the library
- CMake minimum required version raised to v3.13
- Need a fairly recent CMake for C++17 support (>= v3.12)
- [#504](https://github.com/eclipse-paho/paho.mqtt.cpp/issues/504) CMake v3.13 allows INSTALL(TARGETS) to work outside the current directory.
- Clients always created for v5 persistence format, making it universal for any connection.
- If the application specifies a version it is kept as a hint for default connections.
- The version for the connection should be specified in the connect options.
- The `create_options` now have all the parameters to create a client.
- Can specify Server URL, Client ID, and persistence in the create options.
- New client constructor that takes just the options object
- The client caches a const `create_options` struct with all the creation parameters
- Client creation internally simplified without breaking the public API
- Expanded the message constmer to be a full client "event" consumer.
- The events are for *connected, connection_lost, disconnected, message arrived,* and application *shutdown.*
- The application can get client state change notifications without resorting to callbacks.
- There's a new `persistence_type` (std::variant) that can hold any of the persistence specifiers (none, file directory, or user interface).
- Most of the class static constants are now `constexpr`.
- Removed the fake `ReasonCode::MQTTPP_V3_CODE`. Now all reason codes in a v3 connection are SUCCESS.
- The `mqtt::exception` checks if the 'rc' return code actually contains a reason code error, amd if so, sets it as the reason code.
- `property` can now report the `typeid` of its contained value.
- The `properties` list implements a const iterator
- Added a `to_string()` and `operator<<()` for reason codes.
- `thread_queue` is now closable.
- Added documentation for UNIX domain sockets coming in with Paho C v1.3.14
- Removed the manual implementation of `make_unique<>()`
- Added `create_options` assignment operators.
- Fixed some corner cases for topic_filter::matches()
- Cleaned up and fixed a number of example apps.
- Most apps now except a server URI from the command line
- 'data_publish' example uses C++17 std::filesystem for creating a file-based encrypted persistence for messages.
- Updated local CI (buildtst.sh) for current compilers and unit tests.
- Reorganized the source repository
- Completely reformat the sources and added a .clang-format file (a project master and a slightly-different one for headers).
- Added GitHub CI Action, removing legacy Travis and Appveyor files
- [#410](https://github.com/eclipse-paho/paho.mqtt.cpp/issues/410) Added 'shutdown_event' to the event consumer and reworked consumer to prevent propagating exceptions on shutdown.
- [#451](https://github.com/eclipse-paho/paho.mqtt.cpp/issues/451) Added low keep alive to async_publish_time to test connected/connection_lost callbacks
- [#503](https://github.com/eclipse-paho/paho.mqtt.cpp/issues/503) Fixed issue that generated docs were empty.
- [#518](https://github.com/eclipse-paho/paho.mqtt.cpp/pull/518) Add function for checking async consumer event queue size
- [#519](https://github.com/eclipse-paho/paho.mqtt.cpp/pull/519) Fix potential deadlock in set_callback
- [#524](https://github.com/eclipse-paho/paho.mqtt.cpp/issues/524) Fixed copy and move operations for 'subscribe_options'. Added unit tests.
## [Version 1.4.1](https://github.com/eclipse/paho.mqtt.cpp/compare/v1.4.0..v1.4.1) - (2024-07-09)
- [#458](https://github.com/eclipse/paho.mqtt.cpp/issues/458) Set 'disconnected' handler for the consumer queue.
## [Version 1.4.0](https://github.com/eclipse/paho.mqtt.cpp/compare/v1.3.2..v1.4.0) - (2024-06-16)
- Ability to build the Paho C library automatically (now working properly)
- CMake 'PAHO_WITH_MQTT_C' option properly compiles the existing Paho C v1.3.13
- Moved 'src/externals/' to top-level
- Reorganized the source tree:
- Moved header files to top-level 'include/' directory.
- Moved 'src/sampless/' to top-level and renamed 'examples/'
- Fixed and optimized 'topic_matcher' trie collection
- Added some missing Eclipse/Paho legal documents to the repo.
- Ran a spell-checker over the code and doc files.
- [#498](https://github.com/eclipse/paho.mqtt.cpp/issues/498) Overloaded property constructor to also take a uint32_t
- [#491](https://github.com/eclipse/paho.mqtt.cpp/pull/491) add topic_matcher.h to install
- [#485](https://github.com/eclipse/paho.mqtt.cpp/pull/485) export dependencies
- [#484](https://github.com/eclipse/paho.mqtt.cpp/pull/484) add token::get_message
- [#480](https://github.com/eclipse/paho.mqtt.cpp/issues/480) Fixed Paho C version in 'install_paho_mqtt_c.sh' script.
- [#473](https://github.com/eclipse/paho.mqtt.cpp/issues/473) Getter for the client's cached connect options.
- [#466](https://github.com/eclipse/paho.mqtt.cpp/pull/466) Iterable string collection
- [#416](https://github.com/eclipse/paho.mqtt.cpp/issues/416) Removed FindPahoMqttC.cmake. Using Paho C package directly.
## [Version 1.3.2](https://github.com/eclipse/paho.mqtt.cpp/compare/v1.3.1..v1.3.2) - (2023-12-05)
- [#463](https://github.com/eclipse/paho.mqtt.cpp/issues/463) Fixed generator expression for older CMake
- [#378](https://github.com/eclipse/paho.mqtt.cpp/issues/378) Bad LWT message in async_publish.cpp sample.
## [Version 1.3.1](https://github.com/eclipse/paho.mqtt.cpp/compare/v1.3.0..v1.3.1) - (2023-11-23)
- [#462](https://github.com/eclipse/paho.mqtt.cpp/pull/462) Fix version string for version v1.3.x
## [Version 1.3.0](https://github.com/eclipse/paho.mqtt.cpp/compare/v1.2.0..v1.3.0) - (2023-11-22)
- Fixed building and using library as DLL on Windows with MSVC
- Updated License to Eclipse Public License v2.0
- Updated create and connect options to better deal with MQTT protocol version
- Defaulting connect version to v5 if specified in create options.
- Added a `topic_filter` class to match a single filter to specific topics.
- Added a `topic_matcher` class to create a collection of items in a trie structure that can contain items tied to topic filters. (Useful for queues or callbacks per-subscription topic).
- Minor tweaks to prepare for C++20
- Support for Catch2 v3.x for unit tests (v2.x also still supported).
- Changed the sample apps to use the newer "mqtt://" schemas.
- Connect option initializers for v5 and WebSockets.
Fixed Issues and Pull Requests:
- [#343](https://github.com/eclipse/paho.mqtt.cpp/issues/343) async_client::try_consume_message_until taking single parameter fails to compile
- [#445](https://github.com/eclipse/paho.mqtt.cpp/pull/445) Update properties when moving/copying connect options.
- [#325](https://github.com/eclipse/paho.mqtt.cpp/issues/325) Cache connect options in client to keep memory valid for callbacks like SSL on_error()
- [#361](https://github.com/eclipse/paho.mqtt.cpp/issues/361) Added missing LICENSE file to conform to GitHub conventions.
- [#304](https://github.com/eclipse/paho.mqtt.cpp/issues/304) Missing create_options::DFLT_C_STRUCT symbol when linking with MSVC.
- [#429](https://github.com/eclipse/paho.mqtt.cpp/issues/429) Remove declaration of connect_options::to_string() with missing implementation.
- [#411](https://github.com/eclipse/paho.mqtt.cpp/issues/411) Missing virtual keyword for some client methods
- [#444](https://github.com/eclipse/paho.mqtt.cpp/issues/444) Unit tests to check that connect options builder sets properties.
- [#313](https://github.com/eclipse/paho.mqtt.cpp/issues/313) Get unit tests building on Windows. Needed to get rid of make_unique<> for Windows
- [#397](https://github.com/eclipse/paho.mqtt.cpp/issues/397) Doc about clean session in connect_options.h is wrong
- [#442](https://github.com/eclipse/paho.mqtt.cpp/issues/442) g++ complains with multiple definition of static constexpr for mixed C++11/17 builds
- [#445](https://github.com/eclipse/paho.mqtt.cpp/pull/445)Fix copy/move constructor for connect/disconnect opts with properties
- [#425](https://github.com/eclipse/paho.mqtt.cpp/pull/425) Silence warning for unused variable rsp in class `unsubscribe_response`
- [#440](https://github.com/eclipse/paho.mqtt.cpp/pull/440) Fix typos across the project
- [#428](https://github.com/eclipse/paho.mqtt.cpp/issues/428) Fixed type in create_options.h
- [#407](https://github.com/eclipse/paho.mqtt.cpp/pull/407) Fix nodiscard warnings in sync client
- [#385](https://github.com/eclipse/paho.mqtt.cpp/issues/385) Thread queue deadlock with multiple consumers
- [#374](https://github.com/eclipse/paho.mqtt.cpp/pull/374) Add Paho C as a submodeule
- [#350](https://github.com/eclipse/paho.mqtt.cpp/pull/350) avoid adding Paho MQTT C library twice
- [#253](https://github.com/eclipse/paho.mqtt.cpp/issues/253) implicit capture of 'this' via '[=]' is deprecated in C++20
- [#337](https://github.com/eclipse/paho.mqtt.cpp/issues/337) copy/move of caPath_ in ssl_options
- [#330](https://github.com/eclipse/paho.mqtt.cpp/pull/330) added /build/ folder to .gitignore
- [#317](https://github.com/eclipse/paho.mqtt.cpp/issues/317) String constructor using just len instead of end iterator.
- [#323](https://github.com/eclipse/paho.mqtt.cpp/issues/323) Added Session Expiry Interval to v5 chat sample to test.
## [Version 1.2.0](https://github.com/eclipse/paho.mqtt.cpp/compare/v1.1..v1.2.0) - (2020-12-27)
This release bring in some missing MQTT v5 features, brings in support for websocket headers and proxies, ALPN protocol lists, adds the builder pattern for options, and fixes a number of bugs in both the C++ library and the underlying C lib.
Requires Paho C v1.3.8
- Missing MQTT v5 features:
- Ability to add properties to Subscribe and Unsubscribe packets (i.e. subscription identifiers)
- "Disconnected" callback gives reason code and properties for server disconnect
- New `create_options` that can be used to construct a client with new features:
- Send while disconnected before the 1st successful connection
- Output buffer can delete oldest messages when full
- Can choose to clear the persistence store on startup
- Select whether to persist QoS 0 messages
- Started classes to create options using the Builder Pattern, with the `create_options_builder`, `connect_options_builder`, `message_ptr_builder`, etc.
- User-defined websocket HTTP headers.
- HTTP/S proxy support
- Added ALPN protocol support to SSL/TLS options
- SSL/TLS error and PSK callback support
- Update connection callback support (change credentials when using auto-reconnect)
- Updates to the sample apps:
- Overall cleanup with better consistency
- Example of using websockets and a proxy
- User-based file persistence with simple encoding/encryption
- Sharing a client between multiple threads
- Converted the unit tests to use Catch2
- All library exceptions are now properly derived from the `mqtt::exception` base class.
- [#231] Added `on_disconnected` callback to handle receipt of disconnect packet from server.
- [#211, #223, #235] Removed use of Log() function from the Paho C library.
- [#227] Fixed race condition in thread-safe queue
- [#224] & [#255] Subscribing to MQTT v3 broker with array of one topic causes segfault.
- [#282] Ability to build Debian/Ubuntu package
- [#300] Calling `reconnect()` was hanging forever, even when successful. In addition several of the synchronous `client` calls were hanging forever on failure. They now properly throw a `timeout_error` exception.
- Several memory issues and bug fixes from updated Paho C library support.
## Version 1.1 (2019-10-12) ## Version 1.1 (2019-10-12)
@ -12,17 +196,18 @@ This release was primarily to add MQTT v5 support and server responses.
- Properties can also be obtained from server responses to requests such as from a _connect_ call. These are available in the `token` objects when they complete. - Properties can also be obtained from server responses to requests such as from a _connect_ call. These are available in the `token` objects when they complete.
- The client object tracks the desired MQTT version that the app requested and/or is currently connected at. Internally this is now required by the `response_options` the need to distinguish between pre-v5 and post-v5 callback functions. - The client object tracks the desired MQTT version that the app requested and/or is currently connected at. Internally this is now required by the `response_options` the need to distinguish between pre-v5 and post-v5 callback functions.
- MQTT v5 reason codes for requests are available via `token` objects when they complete. They are also available in `exception` objects that are thrown by tokens. - MQTT v5 reason codes for requests are available via `token` objects when they complete. They are also available in `exception` objects that are thrown by tokens.
- Support for subscibe options, like no local subscriptions, etc. - Support for subscribe options, like no local subscriptions, etc.
- Sample applications were added showing how to do basic Remote Procedure Calls (RPC's) with MQTT v5 using the *RESPONSE_TOPIC* and *CORRELATION_DATA* properties. These are *rpc_math_cli* and *rpc_math_srvr* in the _src/samples_ directory. - Sample applications were added showing how to do basic Remote Procedure Calls (RPC's) with MQTT v5 using the *RESPONSE_TOPIC* and *CORRELATION_DATA* properties. These are *rpc_math_cli* and *rpc_math_srvr* in the _src/samples_ directory.
- A sample "chat" application was added, showing how to use subscribe options, such as "no local". - A sample "chat" application was added, showing how to use subscribe options, such as "no local".
- More descriptive error messages (PR #154), integrated into the `mqtt::exception` class. MQTT v5 reason codes are also included in the exceptions when an error occurs. - More descriptive error messages (PR #154), integrated into the `mqtt::exception` class. MQTT v5 reason codes are also included in the exceptions when an error occurs.
- Applications can (finally) get server responses from the various ACK packets. These are available through the tokens after they complete, as `connect_response`, `subscribe_response`, and `unsubscribe_response`. - Applications can (finally) get server responses from the various ACK packets. These are available through the tokens after they complete, as `connect_response`, `subscribe_response`, and `unsubscribe_response`.
- The `topic` objects can be used to subscribe. - The `topic` objects can be used to subscribe.
- Applications can register individual callback functions instead of using a `callback` interface object. This allows easy use of lambda functions for callbacks. - Applications can register individual callback functions instead of using a `callback` interface object. This allows easy use of lambda functions for callbacks.
- The connect options can take a LWT as a plain message, via `connect_options::set_will_message()` - The connect options can take a LWT as a plain message, via `connect_options::set_will_message()`
- New unit tests have started using _Catch2_. - New unit tests have started using _Catch2_.
- Tested with Paho C v1.3.1 - Tested with Paho C v1.3.1
## Version 1.0.1 (2018-12-12) ## Version 1.0.1 (2018-12-12)
This is a bug-fix released aimed mainly at issues with the build system and working towards more "modern" usage of CMake. In addition: This is a bug-fix released aimed mainly at issues with the build system and working towards more "modern" usage of CMake. In addition:
@ -38,7 +223,7 @@ This is a bug-fix released aimed mainly at issues with the build system and work
The initial Paho C++ Client library for memory-managed platforms (Linux, Windows, etc). The initial Paho C++ Client library for memory-managed platforms (Linux, Windows, etc).
- Requires Paho C Client Library v1.2. - Requires Paho C Client Library v1.2.
- MQTT 3.1 & 3.1.1 - MQTT 3.1 & 3.1.1
- SSL/TLS - SSL/TLS
- Asynchronous & Synchronous interfaces - Asynchronous & Synchronous interfaces
- Persistence and off-line buffering - Persistence and off-line buffering

View File

@ -5,15 +5,15 @@
#******************************************************************************* #*******************************************************************************
# This is part of the Paho MQTT C++ client library. # This is part of the Paho MQTT C++ client library.
# #
# Copyright (c) 2017-2025, Frank Pagliughi
# Copyright (c) 2016-2017, Guilherme Maciel Ferreira # Copyright (c) 2016-2017, Guilherme Maciel Ferreira
# Copyright (c) 2017, Frank Pagliughi
# #
# All rights reserved. This program and the accompanying materials # All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0 # are made available under the terms of the Eclipse Public License v2.0
# and Eclipse Distribution License v1.0 which accompany this distribution. # and Eclipse Distribution License v1.0 which accompany this distribution.
# #
# The Eclipse Public License is available at # The Eclipse Public License is available at
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v20.html
# and the Eclipse Distribution License is available at # and the Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php. # http://www.eclipse.org/org/documents/edl-v10.php.
# #
@ -22,54 +22,101 @@
# Frank Pagliughi # Frank Pagliughi
#*******************************************************************************/ #*******************************************************************************/
## Note: on OS X you should install XCode and the associated command-line tools cmake_minimum_required(VERSION 3.13)
## cmake flags project(PahoMqttCpp VERSION "1.5.1")
cmake_minimum_required(VERSION 3.5)
## project name
project("paho-mqtt-cpp"
VERSION "1.1.0"
LANGUAGES CXX
)
## --- Build options --- ## --- Build options ---
if(WIN32) if(WIN32)
option(PAHO_BUILD_STATIC "Build static library" TRUE) option(PAHO_BUILD_STATIC "Build static library" TRUE)
option(PAHO_BUILD_SHARED "Build shared library (DLL)" FALSE) option(PAHO_BUILD_SHARED "Build shared library (DLL)" FALSE)
option(PAHO_WITH_SSL "Build SSL-enabled library" FALSE) option(PAHO_WITH_SSL "Build SSL-enabled library" FALSE)
else() else()
option(PAHO_BUILD_STATIC "Build static library" FALSE) option(PAHO_BUILD_STATIC "Build static library" FALSE)
option(PAHO_BUILD_SHARED "Build shared library" TRUE) option(PAHO_BUILD_SHARED "Build shared library" TRUE)
option(PAHO_WITH_SSL "Build SSL-enabled library" TRUE) option(PAHO_WITH_SSL "Build SSL-enabled library" TRUE)
option(PAHO_BUILD_DEB_PACKAGE "Build debian package" FALSE)
endif() endif()
option(PAHO_BUILD_SAMPLES "Build sample programs" FALSE) option(PAHO_BUILD_SAMPLES "Build sample/example programs" FALSE)
option(PAHO_BUILD_TESTS "Build tests" FALSE) option(PAHO_BUILD_EXAMPLES "Build sample/example programs" FALSE)
option(PAHO_BUILD_TESTS "Build tests (requires Catch2)" FALSE)
option(PAHO_BUILD_DOCUMENTATION "Create and install the API documentation (requires Doxygen)" FALSE) option(PAHO_BUILD_DOCUMENTATION "Create and install the API documentation (requires Doxygen)" FALSE)
option(PAHO_WITH_MQTT_C "Build Paho C from the internal GIT submodule." FALSE)
## --- C++11 build flags --- if(NOT PAHO_BUILD_SHARED AND NOT PAHO_BUILD_STATIC)
message(FATAL_ERROR "You must set either PAHO_BUILD_SHARED, PAHO_BUILD_STATIC, or both")
endif()
set(CMAKE_CXX_STANDARD 11) # --- Setting naming variables ---
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Generate position-independent code (-fPIC on UNIX) set(PAHO_MQTTPP_GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# --- System Libraries --- ## --- Find Paho C or build it, if reqested ---
if(PAHO_WITH_SSL)
find_package(OpenSSL REQUIRED)
set(PAHO_MQTT_C_LIB eclipse-paho-mqtt-c::paho-mqtt3as)
else()
set(PAHO_MQTT_C_LIB eclipse-paho-mqtt-c::paho-mqtt3a)
endif()
if(PAHO_WITH_MQTT_C)
message(STATUS "Paho C: Bundled")
## Build the Paho C library from the submodule
set(PAHO_ENABLE_TESTING FALSE CACHE BOOL "No Paho C tests")
set(PAHO_HIGH_PERFORMANCE TRUE CACHE BOOL "Paho C high performance")
if(NOT WIN32)
set(PAHO_WITH_UNIX_SOCKETS TRUE CACHE BOOL "Support for Unix-domain sockets")
endif()
add_subdirectory(${PROJECT_SOURCE_DIR}/externals/paho-mqtt-c)
## Alias namespace so that the full names can be used with the subdir.
if(PAHO_BUILD_SHARED)
add_library(eclipse-paho-mqtt-c::paho-mqtt3a ALIAS paho-mqtt3a)
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3a)
if(PAHO_WITH_SSL)
add_library(eclipse-paho-mqtt-c::paho-mqtt3as ALIAS paho-mqtt3as)
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3as)
endif()
endif()
if(PAHO_BUILD_STATIC)
add_library(eclipse-paho-mqtt-c::paho-mqtt3a-static ALIAS paho-mqtt3a-static)
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3a-static)
if(PAHO_WITH_SSL)
add_library(eclipse-paho-mqtt-c::paho-mqtt3as-static ALIAS paho-mqtt3as-static)
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3as-static)
endif()
endif()
## install paho.mqtt.c library (appending to PahoMqttCpp export)
install(TARGETS ${PAHO_MQTT_C_LIBS}
EXPORT PahoMqttCpp
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
else()
find_package(eclipse-paho-mqtt-c REQUIRED)
endif()
# --- System Details ---
include(GNUInstallDirs) include(GNUInstallDirs)
if(WIN32) if(WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(LIBS_SYSTEM ws2_32) set(LIBS_SYSTEM ws2_32)
elseif(UNIX)
set(LIBS_SYSTEM c stdc++)
endif() endif()
## --- Build directories --- # --- The headers ---
add_subdirectory(include/mqtt)
# For the paho_mqtt_c module # For the paho_mqtt_c module
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
@ -81,36 +128,44 @@ if(PAHO_BUILD_DOCUMENTATION)
add_subdirectory(doc) add_subdirectory(doc)
endif() endif()
# --- Default library for samples and unit tests --- # --- Example Apps ---
if(PAHO_BUILD_SHARED) if(PAHO_BUILD_SAMPLES OR PAHO_BUILD_EXAMPLES)
set(PAHO_CPP_LIB paho-mqttpp3) add_subdirectory(examples)
else()
set(PAHO_CPP_LIB paho-mqttpp3-static)
endif()
# --- Sample Apps ---
if(PAHO_BUILD_SAMPLES)
add_subdirectory(src/samples)
endif() endif()
# --- Unit Tests --- # --- Unit Tests ---
if(PAHO_BUILD_TESTS) if(PAHO_BUILD_TESTS)
enable_testing()
add_subdirectory(test/unit) add_subdirectory(test/unit)
add_subdirectory(test/cppunit)
endif() endif()
## --- Install generated header(s) ---
install(
DIRECTORY
${PAHO_MQTTPP_GENERATED_DIR}/include/
DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}
)
## --- Packaging settings --- ## --- Packaging settings ---
if(WIN32) if(WIN32)
set(CPACK_GENERATOR "ZIP") set(CPACK_GENERATOR "ZIP")
elseif(UNIX) elseif(UNIX)
set(CPACK_GENERATOR "TGZ") if(PAHO_BUILD_DEB_PACKAGE)
set(CPACK_GENERATOR "DEB")
include(cmake/CPackDebConfig.cmake)
else()
set(CPACK_GENERATOR "TGZ")
endif()
endif() endif()
include(CPack) include(CPack)
# --- Export CMake TARGETS ---
add_subdirectory(cmake) add_subdirectory(cmake)

47
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,47 @@
# Community Code of Conduct
**Version 1.2
August 19, 2020**
## Our Pledge
In the interest of fostering an open and welcoming environment, we as community members, contributors, committers, and project leaders pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
With the support of the Eclipse Foundation staff (the “Staff”), project committers and leaders are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project committers and leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the Eclipse Foundation project or its community in public spaces. Examples of representing a project or community include posting via an official social media account, or acting as a project representative at an online or offline event. Representation of a project may be further defined and clarified by project committers, leaders, or the EMO.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Staff at codeofconduct@eclipse.org. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The Staff is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project committers or leaders who do not follow the Code of Conduct in good faith may face temporary or permanent repercussions as determined by the Staff.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org) , version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct/)

View File

@ -18,13 +18,13 @@ The Paho C++ library attempts to follow the naming conventions of the C++ standa
- Class names are lower snake case: *classes_like_this* - Class names are lower snake case: *classes_like_this*
- Function names are lower snake case: *functions_like_this* - Function names are lower snake case: *functions_like_this*
- Variable names are lower camel case: *varsLikeThis* - Variable names are lower camel case: *varsLikeThis*
- Class memeber are lower camel case with a trailing underscore: *memVarsLikeThis_* - Class members are lower camel case with a trailing underscore: *memVarsLikeThis_*
- Constants are all caps: *CONSTANTS_LIKE_THIS* - Constants are all caps: *CONSTANTS_LIKE_THIS*
## Format Conventions ## Format Conventions
The top-level project directory contains a _.editorconfig_ file with some basic hints as to formatting conventions for source files. The top-level project directory contains a _.editorconfig_ file with some basic hints as to formatting conventions for source files.
A few minutes looking through the existing sources will reveal the basic styles used. Pull Requests should generally try to fit in to the existing style and not try to impose new ones. A few minutes looking through the existing sources will reveal the basic styles used. Pull Requests should generally try to fit in to the existing style and not try to impose new ones.
At some point in the future, a formatter may be added to the project (probably Clang format), but until then, any sources that can be fixed up with an automated code generator or beautifier would generally be accepted into the project. But at some point soon after would then be reformat to fit into the overall conventions. At some point in the future, a formatter may be added to the project (probably Clang format), but until then, any sources that can be fixed up with an automated code generator or beautifier would generally be accepted into the project. But at some point soon after would then be reformat to fit into the overall conventions.

View File

@ -1,47 +1,66 @@
Contributing to Paho # Contributing to Paho
====================
Thanks for your interest in this project!
Thanks for your interest in this project.
You can contribute bugfixes and new features by sending pull requests through GitHub.
Project description:
-------------------- ## Legal
The Paho project has been created to provide scalable open-source implementations of open and standard messaging protocols aimed at new, existing, and emerging applications for Machine-to-Machine (M2M) and Internet of Things (IoT). In order for your contribution to be accepted, it must comply with the Eclipse Foundation IP policy.
Paho reflects the inherent physical and cost constraints of device connectivity. Its objectives include effective levels of decoupling between devices and applications, designed to keep markets open and encourage the rapid growth of scalable Web and Enterprise middleware and applications. Paho is being kicked off with MQTT publish/subscribe client implementations for use on embedded platforms, along with corresponding server support as determined by the community.
Please read the [Eclipse Foundation policy on accepting contributions via Git](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git).
- https://projects.eclipse.org/projects/technology.paho
1. Sign the [Eclipse ECA](http://www.eclipse.org/legal/ECA.php)
Developer resources: 1. Register for an Eclipse Foundation User ID. You can register [here](https://dev.eclipse.org/site_login/createaccount.php).
-------------------- 2. Log into the [Eclipse projects forge](https://www.eclipse.org/contribute/cla), and click on 'Eclipse Contributor Agreement'.
2. Go to your [account settings](https://dev.eclipse.org/site_login/myaccount.php#open_tab_accountsettings) and add your GitHub username to your account.
Information regarding source code management, builds, coding standards, and more. 3. Make sure that you _sign-off_ your Git commits in the following format:
``` Signed-off-by: Alex Smith <alexsmith@nowhere.com> ``` This is usually at the bottom of the commit message. You can automate this by adding the '-s' flag when you make the commits. e.g. ```git commit -s -m "Adding a cool feature"```
- https://projects.eclipse.org/projects/technology.paho/developer 4. Ensure that the email address that you make your commits with is the same one you used to sign up to the Eclipse Foundation website with.
Contributor License Agreement: ## Contributing a change
------------------------------
1. [Fork the repository on GitHub](https://github.com/eclipse/paho.mqtt.cpp/fork)
Before your contribution can be accepted by the project, you need to create and electronically sign the Eclipse Foundation Contributor License Agreement (CLA). 2. Clone the forked repository onto your computer: ``` git clone https://github.com/<your username>/paho.mqtt.cpp.git ```
3. Create a new branch from the latest ```develop``` branch with ```git checkout -b YOUR_BRANCH_NAME origin/develop```
- http://www.eclipse.org/legal/CLA.php 4. Make your changes
5. If developing a new feature, make sure to include unit tests.
Contact: 6. Ensure that all new and existing tests pass.
-------- 7. Commit the changes into the branch: ``` git commit -s ``` Make sure that your commit message is meaningful and describes your changes correctly.
8. If you have a lot of commits for the change, squash them into a single / few commits.
Contact the project developers via the project's "dev" list. 9. Push the changes in your branch to your forked repository.
10. Finally, go to [https://github.com/eclipse/paho.mqtt.cpp](https://github.com/eclipse/paho.mqtt.cpp) and create a pull request from your "YOUR_BRANCH_NAME" branch to the ```develop``` one to request review and merge of the commits in your pushed branch.
- https://dev.eclipse.org/mailman/listinfo/paho-dev
Search for bugs: What happens next depends on the content of the patch. If it is 100% authored
---------------- by the contributor and is less than 1000 lines (and meets the needs of the
project), then it can be pulled into the main repository. If not, more steps
This project uses Bugzilla to track ongoing development and issues. are required. These are detailed in the
[legal process poster](http://www.eclipse.org/legal/EclipseLegalProcessPoster.pdf).
- https://bugs.eclipse.org/bugs/buglist.cgi?product=Paho
Create a new bug:
----------------- ## Developer resources:
Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome!
Information regarding source code management, builds, coding standards, and more.
- https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Paho
- [https://projects.eclipse.org/projects/iot.paho/developer](https://projects.eclipse.org/projects/iot.paho/developer)
Contact:
--------
Contact the project developers via the project's development
[mailing list](https://dev.eclipse.org/mailman/listinfo/paho-dev).
Search for bugs:
----------------
This project uses GitHub Issues here: [github.com/eclipse/paho.mqtt.cpp/issues](https://github.com/eclipse/paho.mqtt.cpp/issues) to track ongoing development and issues.
Create a new bug:
-----------------
Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome!
- [Create new Paho bug](https://github.com/eclipse/paho.mqtt.cpp/issues/new)

13
LICENSE Normal file
View File

@ -0,0 +1,13 @@
Eclipse Public License - v 2.0
This program and the accompanying materials
are made available under the terms of the Eclipse Public License v2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-20/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
For an explanation of what dual-licensing means to you, see:
https://www.eclipse.org/legal/eplfaq.php#DUALLIC

55
NOTICE Normal file
View File

@ -0,0 +1,55 @@
# Notices for Eclipse Paho
This content is produced and maintained by the Eclipse Paho project.
* Project home: https://projects.eclipse.org/projects/iot.paho
## Trademarks
Paho™ is a trademark of the Eclipse Foundation.
## Copyright
All content is the property of the respective authors or their employers. For
more information regarding authorship of content, please consult the listed
source code repository logs.
## Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License v2.0 which is available at
https://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
v1.0 which is available at https://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
## Source Code
The project maintains the following source code repositories:
* https://github.com/eclipse/paho-website
* https://github.com/eclipse/paho.golang
* https://github.com/eclipse/paho.mqtt-sn.embedded-c
* https://github.com/eclipse/paho.mqtt-spy
* https://github.com/eclipse/paho.mqtt.android
* https://github.com/eclipse/paho.mqtt.c
* https://github.com/eclipse/paho.mqtt.cpp
* https://github.com/eclipse/paho.mqtt.d
* https://github.com/eclipse/paho.mqtt.embedded-c
* https://github.com/eclipse/paho.mqtt.golang
* https://github.com/eclipse/paho.mqtt.java
* https://github.com/eclipse/paho.mqtt.javascript
* https://github.com/eclipse/paho.mqtt.m2mqtt
* https://github.com/eclipse/paho.mqtt.python
* https://github.com/eclipse/paho.mqtt.ruby
* https://github.com/eclipse/paho.mqtt.rust
* https://github.com/eclipse/paho.mqtt.testing
## Cryptography
Content may contain encryption software. The country in which you are currently
may have restrictions on the import, possession, and use, and/or re-export to
another country, of encryption software. BEFORE using any encryption software,
please check the country's laws, regulations and policies concerning the import,
possession, or use, and re-export of encryption software, to see if this is
permitted.

440
README.md
View File

@ -1,109 +1,137 @@
# Eclipse Paho MQTT C++ Client Library # Eclipse Paho MQTT C++ Client Library
[![Build Status](https://travis-ci.org/eclipse/paho.mqtt.cpp.svg?branch=master)](https://travis-ci.org/eclipse/paho.mqtt.cpp) This repository contains the source code for the [Eclipse Paho](http://eclipse.org/paho) MQTT C++ client library for memory-managed operating systems such as Linux, MacOS, and Windows.
This repository contains the source code for the [Eclipse Paho](http://eclipse.org/paho) MQTT C++ client library on memory-managed operating systems such as Linux/Posix and Windows. This code builds a library which enables Modern C++ applications (C++17 and beyond) to connect to an [MQTT](http://mqtt.org) broker, publish messages, subscribe to topics, and receive messages from the broker.
This code builds a library which enables C++11 applications to connect to an [MQTT](http://mqtt.org) broker, publish messages to the broker, and to subscribe to topics and receive published messages.
The library has the following features: The library has the following features:
- Support for MQTT v5, v3.1.1, and v 3.1 - Support for MQTT v3.1, v3.1.1, and v5.
- TCP, SSL/TLS, and WebSocket transports - Network Transports:
- Standard TCP
- UNIX-domain sockets
- Secure sockets with SSL/TLS
- WebSockets
- Secure and insecure
- Proxy support
- Message persistence - Message persistence
- Automatic reconnect - User configurable
- Offline buffering - Built-in File persistence
- High availability - User-defined key/value persistence easy to implement
- Blocking and non-blocking API's - Automatic Reconnect
- Offline Buffering
- High Availability
- Blocking and non-blocking APIs
- Modern C++ interface (C++17)
This code requires the [Paho C library](https://github.com/eclipse/paho.mqtt.c) by Ian Craggs, et al., specifically version 1.3.1 or possibly later. This code requires the [Paho C library](https://github.com/eclipse/paho.mqtt.c) by Ian Craggs, et al., specifically version 1.3.14 or possibly later.
## Latest News ## Latest News
To keep up with the latest announcements for this project, or to ask questions: To keep up with the latest announcements for this project, or to ask questions:
**Twitter:** [@eclipsepaho](https://twitter.com/eclipsepaho) and [@fmpagliughi](https://twitter.com/fmpagliughi) **Email:** [Eclipse Paho Mailing List](https://accounts.eclipse.org/mailing-list/paho-dev)
**EMail:** [Eclipse Paho Mailing List](https://accounts.eclipse.org/mailing-list/paho-dev) ### What's New in v1.5.x
**Mattermost:** [Eclipse Mattermost Paho Channel](https://mattermost.eclipse.org/eclipse/channels/paho) The latest updates for v1.5 moved the codebase to C++17 and added support for UNIX-domain sockets. They also fixed a number of build issues, now targeting the latest Paho C release, v1.3.14.
The primary changes in the v1.5 versions are:
### New Features in Paho C++ v1.1 - Updated the code base to C++17
- Support for the Paho C v1.3.14 release.
- Support for UNIX-domain sockets
- Reorganize and reformat the sources and added a .clang-format capability.
- Create universal client instances that can connect using v3 or v5. (i.e. no more instances that are only v3 capable)
- Bump the CMake to v3.13
- Fix a large number of CMake build issues
- Updated the GitHub CI
- MQTT v5 support: For the full list of updates in each release, see the [CHANGELOG](https://github.com/eclipse-paho/paho.mqtt.cpp/blob/master/CHANGELOG.md).
- **Properties**
- New `property` class acts something like a std::variant to hold a property of any supported type.
- New `properties` class is a collection type to hold all the properties for a single transmitted packet.
- Properties can be added to outbound messages and obtained from received messages.
- Properties can also be obtained from server responses to requests such as from a _connect_ call. These are available in the `token` objects when they complete.
- The client object tracks the desired MQTT version that the app requested and/or is currently connected at. Internally this is now required by the `response_options` the need to distinguish between pre-v5 and post-v5 callback functions.
- MQTT v5 reason codes for requests are available via `token` objects when they complete. They are also available in `exception` objects that are thrown by tokens.
- Support for subscibe options, like no local subscriptions, etc.
- Sample applications were added showing how to do basic Remote Procedure Calls (RPC's) with MQTT v5 using the *RESPONSE_TOPIC* and *CORRELATION_DATA* properties. These are *rpc_math_cli* and *rpc_math_srvr* in the _src/samples_ directory.
- A sample "chat" application was added, showing how to use subscribe options, such as "no local".
- More descriptive error messages (PR #154), integrated into the `mqtt::exception` class. MQTT v5 reason codes are also included in the exceptions when an error occurs.
- Applications can (finally) get server responses from the various ACK packets. These are available through the tokens after they complete, as `connect_response`, `subscribe_response`, and `unsubscribe_response`.
- The `topic` objects can be used to subscribe.
- Applications can register individual callback functions instead of using a `callback` interface object. This allows easy use of lambda functions for callbacks.
- The connect options can take a LWT as a plain message, via `connect_options::set_will_message()`
### _Catch2_ Unit Tests
Unit tests are being converted to use _Catch2_ for the test framework. The legacy unit tests are still using _CppUnit_, compiled into a separate test executable. If everything goes well with _Catch2_, the older unit tests will be ported to _Catch2_ as well.
_Catch2_ can be found here: [Catch2](https://github.com/catchorg/Catch2)
## Contributing ## Contributing
Contributions to this project are gladly welcomed. Before submitting a Pull Request, please keep two things in mind: Contributions to this project are gladly welcomed and appreciated. Before submitting a Pull Request, please keep three things in mind:
- This is an official Eclipse project, so it is required that all contributors sign an [Eclipse Contributor Agreement (ECA)](https://www.eclipse.org/legal/ECA.php) - This is an official Eclipse project, so it is required that all contributors sign an [Eclipse Contributor Agreement (ECA)](https://www.eclipse.org/legal/ECA.php)
- Please submit all Pull Requests against the _develop_ branch (not master). - Please submit all Pull Requests against the _develop_ branch (not master).
- Please sign all commits.
For full details, see [CONTRIBUTING.md](https://github.com/eclipse/paho.mqtt.cpp/blob/master/CONTRIBUTING.md). For full details, see [CONTRIBUTING.md](https://github.com/eclipse/paho.mqtt.cpp/blob/master/CONTRIBUTING.md).
## Building from source ## Building from source
*GNU Make and autotools were deprecated and removed in the v1.1 release.* As of v1.5, the Paho C++ library uses C++17 features, thus requiring a fully compliant C++17 compiler. Some of the more common compilers that can be used, depending on the target platform, are:
_CMake_ is a cross-platform build system suitable for Unix and non-Unix platforms such as Microsoft Windows. It is now the only supported build system. * GCC v8 or later
* _clang_ v5 or later
* Visual Studio 2017 15.8 (MSVC 19.15) or later
The Paho C++ library requires the Paho C library, v1.3.1 or greater, to be built and installed first. More information below. _CMake_ is a cross-platform build system suitable for Unix and non-Unix platforms such as Microsoft Windows. It is the only supported build system. The current supported minimum version is:
* cmake v3.13
The Paho C++ library requires the Paho C library, v1.3.14 or greater to be built and installed. That can be done before building this library, or it can be done here using the CMake `PAHO_WITH_MQTT_C` build option to build both libraries at the same time. This also guarantees that a proper version of the C library is used, and that it is build with compatible options.
### Build Options
CMake allows for options to direct the build. The following are specific to Paho C++: CMake allows for options to direct the build. The following are specific to Paho C++:
Variable | Default Value | Description Variable | Default Value | Description
------------ | ------------- | ------------- ------------ | ------------- | -------------
PAHO_BUILD_SHARED | TRUE (Linux), FALSE (Win32) | Whether to build the shared library PAHO_BUILD_SHARED | TRUE (*nix), FALSE (Win32) | Whether to build the shared library
PAHO_BUILD_STATIC | FALSE (Linux), TRUE (Win32) | Whether to build the static library PAHO_BUILD_STATIC | FALSE (*nix), TRUE (Win32) | Whether to build the static library
PAHO_BUILD_DOCUMENTATION | FALSE | Create and install the HTML based API documentation (requires Doxygen) PAHO_WITH_SSL | TRUE (*nix), FALSE (Win32) | Whether to build SSL/TLS support into the library
PAHO_BUILD_SAMPLES | FALSE | Build sample programs PAHO_BUILD_DOCUMENTATION | FALSE | Create the HTML API documentation (requires _Doxygen_)
PAHO_BUILD_TESTS | FALSE | Build the unit tests. (This currently requires both _CppUnit_ and _Catch2_) PAHO_BUILD_EXAMPLES | FALSE | Whether to build the example programs
PAHO_WITH_SSL | TRUE (Linux), FALSE (Win32) | Flag that defines whether to build ssl-enabled binaries too PAHO_BUILD_TESTS | FALSE | Build the unit tests. (Requires _Catch2_)
PAHO_BUILD_DEB_PACKAGE | FALSE | Flag that configures cpack to build a Debian/Ubuntu package
PAHO_WITH_MQTT_C | FALSE | Whether to build the bundled Paho C library
In addition, the C++ build might commonly use `CMAKE_PREFIX_PATH` to help the build system find the location of the Paho C library. Enabling `PAHO_WITH_MQTT_C` builds and links in the Paho C library using compatible build options. If this is enabled, it passes the `PAHO_WITH_SSL` option to the C library, and also sets the options `PAHO_HIGH_PERFORMANCE` and `PAHO_WITH_UNIX_SOCKETS` for the C lib. These can be disabled in the cache before building if desired.
### Unix and Linux In addition, the C++ build might commonly use `CMAKE_PREFIX_PATH` to help the build system find the location of the Paho C library if it was built separately.
### Build the Paho C++ and Paho C libraries together
The quickest and easiest way to build Paho C++ is to build it together with Paho C in a single step using the included Git submodule.
This requires the CMake option `PAHO_WITH_MQTT_C` set.
```
$ git clone https://github.com/eclipse/paho.mqtt.cpp
$ cd paho.mqtt.cpp
$ git co v1.5.2
$ git submodule init
$ git submodule update
$ cmake -Bbuild -H. -DPAHO_WITH_MQTT_C=ON -DPAHO_BUILD_EXAMPLES=ON
$ sudo cmake --build build/ --target install
```
This assumes the build tools and dependencies, such as OpenSSL, have already been installed. For more details and platform-specific requirements, see below.
### Unix-style Systems (Linux, macOS, etc)
On *nix systems CMake creates Makefiles. On *nix systems CMake creates Makefiles.
The build process currently supports a number of Unix and Linux flavors. The build process requires the following tools: The build process currently supports a number of Unix and Linux flavors. The build process requires the following tools:
* CMake v3.5 or newer * CMake v3.13 or newer
* GCC v4.8 or newer or Clang v3.9 or newer * A fully-compatible C++17 compiler. Common options are:
* GNU Make * GCC v8 or later
* _clang_ v5 or later
On Debian based systems this would mean that the following packages have to be installed: On Debian based systems this would mean that the following packages have to be installed:
``` ```
$ sudo apt-get install build-essential gcc make cmake cmake-gui cmake-curses-gui $ sudo apt-get install build-essential gcc make cmake
``` ```
If you will be using secure sockets (and you probably should): If you will be using secure sockets (and you probably should if you're sending messages across a public netwok):
``` ```
$ sudo apt-get install libssl-dev $ sudo apt-get install libssl-dev
``` ```
Building the documentation requires doxygen and optionally graphviz to be installed: Building the documentation requires doxygen and optionally graphviz to be installed:
@ -112,178 +140,189 @@ Building the documentation requires doxygen and optionally graphviz to be instal
$ sudo apt-get install doxygen graphviz $ sudo apt-get install doxygen graphviz
``` ```
Unite tests are currently being built using both _CppUnit_ and _Catch2_. The _CppUnit_ tests are being deprecated and replaced with _Catch2_ equivalents. In the meantime, however, both systems are required to build the tests. Unit tests are built using _Catch2_.
_Catch2_ can be found here: [Catch2](https://github.com/catchorg/Catch2). You must download and install _Catch2_ to build and run the unit tests locally. Currently _Catch2_ versions v2.x and v3.x are supported.
#### Building the Paho C library
The Paho C library can be built automatically when building this library by enabling the CMake build option, `PAHO_WITH_MQTT_C`. That will build and install the Paho C library from a Git submodule, using a known-good version, and the proper build configuration for the C++ library. But iIf you want to manually specify the build configuration of the Paho C library or use a different version, then it must be built and installed before building the C++ library. Note, this version of the C++ library requires Paho C v1.3.14 or greater.
To download and build the Paho C library:
$ git clone https://github.com/eclipse/paho.mqtt.c.git
$ cd paho.mqtt.c
$ git checkout v1.3.14
$ cmake -Bbuild -H. -DPAHO_ENABLE_TESTING=OFF -DPAHO_WITH_SSL=ON -DPAHO_HIGH_PERFORMANCE=ON
$ sudo cmake --build build/ --target install
This builds the C library with SSL/TLS enabled. If that is not desired, omit the `-DPAHO_WITH_SSL=ON`.
It also uses the "high performance" option of the C library to disable more extensive internal memory checks. Remove the _PAHO_HIGH_PERFORMANCE_ option (i.e. turn it off) to debug memory issues, but for most production systems, leave it on for better performance.
The above will install the library to the default location on the host, which for Linux is normally `/usr/local`. To install the library to a non-standard location, use the `CMAKE_INSTALL_PREFIX` to specify a location. For example, to install into a directory under the user's home directory, perhaps for local testing, do this:
$ cmake -Bbuild -H. -DPAHO_ENABLE_TESTING=OFF \
-DPAHO_WITH_SSL=ON -DPAHO_HIGH_PERFORMANCE=ON \
-DCMAKE_INSTALL_PREFIX=$HOME/install
#### Building the Paho C++ library
If the Paho C library is not already installed, the recommended version can be built along with the C++ library in a single step using the CMake option `PAHO_WITH_MQTT_C` set on.
$ git clone https://github.com/eclipse/paho.mqtt.cpp
$ cd paho.mqtt.cpp
$ git co v1.5.2
$ git submodule init
$ git submodule update
$ cmake -Bbuild -H. -DPAHO_WITH_MQTT_C=ON -DPAHO_BUILD_EXAMPLES=ON
$ sudo cmake --build build/ --target install
If a recent version of the Paho C library is available on the build host, and it's installed to a default location, it does not need to be built again. Omit the `PAHO_WITH_MQTT_C` option:
$ cmake -Bbuild -H. -DPAHO_BUILD_SAMPLES=ON
If the Paho C library is installed to a _non-default_ location, or you want to build against a different version, use the `CMAKE_PREFIX_PATH` to specify its install location. Perhaps something like this:
$ cmake -Bbuild -H. -DPAHO_BUILD_SAMPLES=ON -DCMAKE_PREFIX_PATH=$HOME/install
#### Building a Debian/Ubuntu package
A Debian/Ubuntu install `.deb` file can be created as follows:
``` ```
$ sudo apt-get install libcppunit-dev $ cmake -Bbuild -H. -DPAHO_WITH_SSL=ON -DPAHO_ENABLE_TESTING=OFF -DPAHO_BUILD_DEB_PACKAGE=ON
``` $ cmake --build build
$ (cd build && cpack)
_Catch2_ can be found here: [Catch2](https://github.com/catchorg/Catch2)
Before building the C++ library, first, build and install the Paho C library:
```
$ git clone https://github.com/eclipse/paho.mqtt.c.git
$ cd paho.mqtt.c
$ git checkout v1.3.1
$ cmake -Bbuild -H. -DPAHO_WITH_SSL=ON -DPAHO_ENABLE_TESTING=OFF
$ sudo cmake --build build/ --target install
$ sudo ldconfig
```
This builds with SSL/TLS enabled. If that is not desired, omit the `-DPAHO_WITH_SSL=ON`.
If you installed the C library on a non-standard path, you might want to pass it as value to the `CMAKE_PREFIX_PATH` option.
Using these variables CMake can be used to generate your Makefiles. The out-of-source build is the default on CMake. Therefore it is recommended to invoke all build commands inside your chosen build directory.
An example build session might look like this:
```
$ git clone https://github.com/eclipse/paho.mqtt.cpp
$ cd paho.mqtt.cpp
$ cmake -Bbuild -H. -DPAHO_BUILD_DOCUMENTATION=TRUE -DPAHO_BUILD_SAMPLES=TRUE
$ sudo cmake --build build/ --target install
$ sudo ldconfig
```
If you did not install Paho C library to a default system location or you want to build against a different version, use the `CMAKE_PREFIX_PATH` to specify its install location:
```
$ cmake -Bbuild -H. -DPAHO_BUILD_DOCUMENTATION=TRUE -DPAHO_BUILD_SAMPLES=TRUE \
-DCMAKE_PREFIX_PATH=../../paho.mqtt.c/build/install/usr/local
```
To use another compiler, either the CXX environment variable can be specified in the configuration step:
```
$ CXX=clang++ cmake ..
```
or the `CMAKE_CXX_COMPILER` flag can be used:
```
$ cmake -DCMAKE_CXX_COMPILER=clang++
```
#### Updating CMake on Ubuntu 14.04 or 16.04
The versions of CMake on Ubuntu 14.04 or 16.04 LTS are pretty old and have some problems with Paho C++ library. A newer version can be added by downloading the source and building it. If the older cmake can be removed from the system using the package manager, or it can be kept, using the Ububtu alternatives to chose between the versions.
For example, here's how to install CMake v3.6 on Ubuntu 14.04, while keeping the older CMake available as _cmake-2.8:_
```
$ wget http://www.cmake.org/files/v3.6/cmake-3.6.3.tar.gz
$ tar -xvzf cmake-3.6.3.tar.gz
$ cd cmake-3.6.3/
$ ./configure
$ make
$ sudo make install
$ sudo mv /usr/bin/cmake /usr/bin/cmake-2.8
$ sudo update-alternatives --install /usr/bin/cmake cmake /usr/local/bin/cmake 100
$ sudo update-alternatives --install /usr/bin/cmake cmake /usr/bin/cmake-2.8 200
$ cmake --version
cmake version 3.6.3
```
You can speed up the CMake build on multi-core systems, by specifying parallel buid jobs for the configure and make steps, above, such as the following for a 4-core system:
```
$ ./configure --parallel=4
$ make -j4
$ sudo make install
``` ```
### Windows ### Windows
On Windows systems CMake creates Visual Studio project files. On Windows, CMake creates Visual Studio project files for use with MSVC. Currently, other compilers like _clang_ or _MinGW_ are not directly supported.
#### Using Paho C++ as a Windows DLL
The project can be built as a static library or shared DLL on Windows. If using it as a DLL in your application, you should define the macro `PAHO_MQTTPP_IMPORTS` before including any Paho C++ include files. Preferably, make it a global definition in the application's build file, like in CMake:
target_compile_definitions(myapp PUBLIC PAHO_MQTTPP_IMPORTS)
It's better not to mix DLLs and static libraries, but if you do link the Paho C++ DLL against the Paho C static library, you may need to manually resolve some system dependencies, like adding the WinSock library as a dependency to your application:
target_link_libraries(myapp ws2_32)
#### Building the Library on Windows
The build process currently supports a number Windows versions. The build process requires the following tools: The build process currently supports a number Windows versions. The build process requires the following tools:
* CMake GUI v3.5 or newer * CMake GUI v3.13 or newer
* Visual Studio 2015 or newer * Visual Studio 2019 or newer
First install and open the cmake-gui application. This tutorial is based on cmake-gui 3.5.2. The libraries can be completely built at an MSBuild Command Prompt. Download the Paho C and C++ library sources, then open a command window and first compile the Paho C library:
Second, select the path to the Paho MQTT C library (CMAKE_PREFIX_PATH) if not installed in a standard path. Remember that the Paho MQTT C must be installed on the system. Next, choose if it is supposed to build the documentation (PAHO_BUILD_DOCUMENTATION) and/or the sample applications (PAHO_BUILD_SAMPLES). > cd paho.mqtt.c
> cmake -Bbuild -H. -DCMAKE_INSTALL_PREFIX=C:\mqtt\paho-c
Once the configuration is done, click on the Configure button, select the version of the Visual Studio, and then click on Generate button. > cmake --build build/ --target install
At the end of this process you have a Visual Studio solution.
Alternately, the libraries can be completely built at an MSBuild Command Prompt. Download the Paho C and C++ library sources, then open a command window and first compile the Paho C library:
```
> cd paho.mqtt.c
> cmake -Bbuild -H. -DCMAKE_INSTALL_PREFIX=C:\mqtt\paho-c
> cmake --build build/ --target install
```
Then build the C++ library: Then build the C++ library:
``` > cd ..\paho.mqtt.cpp
> cd ..\paho.mqtt.cpp > cmake -Bbuild -H. -DCMAKE_INSTALL_PREFIX=C:\mqtt\paho-cpp -DPAHO_BUILD_SAMPLES=ON -DPAHO_WITH_SSL=OFF -DCMAKE_PREFIX_PATH=C:\mqtt\paho-c
> cmake -Bbuild -H. -DCMAKE_INSTALL_PREFIX=C:\mqtt\paho-cpp -DPAHO_BUILD_SAMPLES=ON -DPAHO_WITH_SSL=OFF -DCMAKE_PREFIX_PATH=C:\mqtt\paho-c > cmake --build build/ --target install
> cmake --build build/ --target install
```
This builds and installs both libraries to a non-standard location under `C:\mqtt`. Modify this location as desired or use the default location, but either way, the C++ library will most likely need to be told where the C library was built using `CMAKE_PREFIX_PATH`. This builds and installs both libraries to a non-standard location under `C:\mqtt`. Modify this location as desired or use the default location, but either way, the C++ library will most likely need to be told where the C library was built using `CMAKE_PREFIX_PATH`.
It seems quite odd, but even on a 64-bit system using a 64-bit compiler, MSVC seems to default to a 32-bit build target. It seems quite odd, but even on a 64-bit system using a 64-bit compiler, MSVC seems to default to a 32-bit build target.
The 64-bit target can be selected using tge CMake generator switch, *-G*, at configuration time. The full version must be provided. For Visual Studio 2015 which is v14 do this to first build the Paho C library: The 64-bit target can be selected using the CMake generator switch, *-G*, at configuration time. The full version must be provided.
``` > cmake -G "Visual Studio 16 2019" -Ax64 -Bbuild -H. -DCMAKE_INSTALL_PREFIX=C:\mqtt\paho-c
> cmake -G "Visual Studio 14 Win64" -Bbuild -H. -DCMAKE_INSTALL_PREFIX=C:\mqtt\paho-c > ...
...
```
Then use it to build the C++ library:
```
> cmake -G "Visual Studio 14 Win64" -Bbuild -H. -DCMAKE_INSTALL_PREFIX=C:\mqtt\paho-cpp -DPAHO_WITH_SSL=OFF -DCMAKE_PREFIX_PATH=C:\mqtt\paho-c
...
```
*Note that it is very important that you use the same generator (target) to build BOTH libraries, otherwise you will get lots of linker errors when you try to build the C++ library.* *Note that it is very important that you use the same generator (target) to build BOTH libraries, otherwise you will get lots of linker errors when you try to build the C++ library.*
## Supported Network Protocols
## Example The library supports connecting to an MQTT server/broker using TCP, SSL/TLS, and websockets both (secure and insecure). On *nix targets, UNIX-domain sockets are also supported. The underlying transport is chosen by the URI supplied to indicate the remote host. It can be specified as:
Sample applications can be found in the source repository at _src/samples_: "mqtt://<host>:<port>" - TCP, unsecure
https://github.com/eclipse/paho.mqtt.cpp/tree/master/src/samples "tcp://<host>:<port>" (same)
This is a partial example of what a typical example might look like: "mqtts://<host>:<port>" - SSL/TLS
"ssl://<host>:<port>" (same)
"ws://<host>:<port>" - Unsecure websockets
"wss://<host>:<port>" - Secure websockets
"unix://<path>" - A UNIX-domain socket on the local machine.
(*nix systems, only)
The "mqtt://" and "tcp://" schemas are identical. They indicate an insecure connection over TCP. The "mqtt://" variation is new for the library, but becoming more common across different MQTT libraries.
Similarly, the "mqtts://" and "ssl://" schemas are identical. They specify a secure connection over SSL/TLS sockets.
Note that to use any of the secure connect options, "mqtts://, "ssl://", or "wss://" you must compile the library with the `PAHO_WITH_SSL=ON` CMake option to include OpenSSL. In addition, you _must_ specify `ssl_options` when you connect to the broker - i.e. you must add an instance of `ssl_options` to the `connect_options` when calling `connect()`.
The use of Unix-domain sockets is only available on *nix-style systems like Linux and macOS. It is not available on Windows. It requires the Paho C library built with the CMake option of PAHO_WITH_UNIX_SOCKETS=ON. This is done by default when building the C library automatically with the Git submodule.
## _Catch2_ Unit Tests
Unit tests use _Catch2_ for the test framework. Versions 2.x and 3.x are supported.
_Catch2_ can be found here: [Catch2](https://github.com/catchorg/Catch2)
## Basics of Thread Safety
Some things to keep in mind when using the library in a multi-threaded application:
- The clients are thread-safe. You can publish/subscribe/etc from multiple threads simultaneously. There are internal mutexes to protect multi-threaded access.
- You should not make a blocking call from within a callback from the library, i.e. anything registered with `set_callback()`, `set_message_callback()`, etc. Callbacks are invoked from the one internal thread that is processing incoming packets from the network. If you make a blocking call that expects an ACK, you will deadlock.
- You can only register one `on_message()` callback per client to receive incoming messages for all of your registered subscriptions. That callback runs in the context of the library thread. If you want to process incoming messages from a different (or multiple) threads:
- Use a consumer queue, or create one or more instances of a thread-safe queue to move the messages around.
- The [thread_queue](https://github.com/eclipse/paho.mqtt.cpp/blob/master/include/mqtt/thread_queue.h) class in the library is a thread-safe queue that you can use for this.
- To route incoming messages by topic:
- Use an instance of the (topic_matcher)[https://github.com/eclipse/paho.mqtt.cpp/blob/master/include/mqtt/topic_matcher.h] collection to create a collection of queues or callback functions to receive messages that match a set of topic filters.
- For MQTT v5 consider using Subscription Identifiers to map incoming messages to callbacks or queues.
- The various data and options structs (like connect_options) are simple data structs. They are not thread protected.
## Examples
Sample applications can be found in the source repository at [examples/](https://github.com/eclipse/paho.mqtt.cpp/tree/master/examples).
These can all be build along with the library by specifying the CMake flag: `-DPAHO_BUILD_EXAMPLES=ON` when configuring the build.
This is a partial example of what a typical application might look like:
```cpp ```cpp
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
sample_mem_persistence persist; sample_mem_persistence persist;
mqtt::client client(ADDRESS, CLIENTID, &persist); mqtt::client cli(ADDRESS, CLIENT_ID, &persist);
callback cb; callback cb;
client.set_callback(cb); cli.set_callback(cb);
mqtt::connect_options connOpts; auto connOpts = mqtt::connect_options_builder()
connOpts.set_keep_alive_interval(20); .keep_alive_interval(20);
connOpts.set_clean_session(true); .clean_session()
.finalize();
try { try {
client.connect(connOpts); cli.connect(connOpts);
// First use a message pointer. // First use a message pointer.
mqtt::message_ptr pubmsg = mqtt::make_message(PAYLOAD1); mqtt::message_ptr pubmsg = mqtt::make_message(PAYLOAD1);
pubmsg->set_qos(QOS); pubmsg->set_qos(QOS);
client.publish(TOPIC, pubmsg); cli.publish(TOPIC, pubmsg);
// Now try with itemized publish. // Now try with itemized publish.
client.publish(TOPIC, PAYLOAD2, strlen(PAYLOAD2)+1, 0, false); cli.publish(TOPIC, PAYLOAD2, strlen(PAYLOAD2)+1, 0, false);
// Disconnect // Disconnect
client.disconnect(); cli.disconnect();
} }
catch (const mqtt::persistence_exception& exc) { catch (const mqtt::persistence_exception& exc) {
cerr << "Persistence Error: " << exc.what() << " [" cerr << "Persistence Error: " << exc.what() << " ["
@ -300,32 +339,7 @@ int main(int argc, char* argv[])
} }
``` ```
-----------
The original API organization and documentation were adapted from:
The Paho Java library
by Dave Locke.
Copyright (c) 2012, IBM Corp
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-----------
This code requires: This code requires:
The Paho C library by Ian Craggs The Paho C library by Ian Craggs, et al.
Copyright (c) 2013-2018, IBM Corp. https://github.com/eclipse/paho.mqtt.c
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.

16
SECURITY.md Normal file
View File

@ -0,0 +1,16 @@
# Security Policy
This project follows the [Eclipse Vulnerability Reporting Policy](https://www.eclipse.org/security/policy.php).
Vulnerabilities are tracked by the Eclipse security team, in cooperation with the project lead.
Fixing vulnerabilities is taken care of by the project committers, with assistance and guidance of the security
team.
## Supported Versions
Eclipse Paho provides security updates for the most recent version only.
## Reporting a Vulnerability
We recommend that in case of suspected vulnerabilities you do not create a GitHub issue, but instead contact the
Eclipse Security Team directly sending an email to security@eclipse.org.

View File

@ -13,7 +13,7 @@
indicated below, the Content is provided to you under the terms and conditions of the indicated below, the Content is provided to you under the terms and conditions of the
Eclipse Public License Version 1.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL"). Eclipse Public License Version 1.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL").
A copy of the EPL is available at A copy of the EPL is available at
<a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> <a href="http://www.eclipse.org/legal/epl-v20.html">http://www.eclipse.org/legal/epl-v20.html</a>
and a copy of the EDL is available at and a copy of the EDL is available at
<a href="http://www.eclipse.org/org/documents/edl-v10.php">http://www.eclipse.org/org/documents/edl-v10.php</a>. <a href="http://www.eclipse.org/org/documents/edl-v10.php">http://www.eclipse.org/org/documents/edl-v10.php</a>.
For purposes of the EPL, "Program" will mean the Content.</p> For purposes of the EPL, "Program" will mean the Content.</p>

View File

@ -1,37 +0,0 @@
version: 1.1.{build}
image: Visual Studio 2015
configuration: Debug
install:
- cmd: >-
git clone https://github.com/eclipse/paho.mqtt.c.git
cd paho.mqtt.c
mkdir build_cmake
cd build_cmake
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
cmake -G "NMake Makefiles" -DCMAKE_INSTALL_PREFIX="C:\Temp\paho-c" -DPAHO_WITH_SSL=TRUE -DPAHO_BUILD_DOCUMENTATION=FALSE -DPAHO_BUILD_SAMPLES=FALSE -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=FALSE ..
nmake install
cd ..\..
build_script:
- cmd: >-
mkdir build_cmake
cd build_cmake
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
cmake -G "NMake Makefiles" -DCMAKE_INSTALL_PREFIX="C:\Temp\paho-cpp" -DCMAKE_PREFIX_PATH="C:\Temp\paho-c" -DPAHO_WITH_SSL=TRUE -DPAHO_BUILD_DOCUMENTATION=FALSE -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=TRUE ..
nmake
nmake install
cd ..

View File

@ -4,20 +4,22 @@
# #
# Build test for the Paho C++ library. # Build test for the Paho C++ library.
# #
# This is a local CI for testing the build on a dev machine.
#
# This test the build with a few compilers on Linux. It does a build using # This test the build with a few compilers on Linux. It does a build using
# CMake, for the library, tests, and examples, then runs the unit tests. # CMake, for the library, tests, and examples, then runs the unit tests.
# This is repeated for each of the compilers in the list. If a particular # This is repeated for each of the compilers in the list. If a particular
# compiler is not installed on the system, it is just skipped. # compiler is not installed on the system, it is just skipped.
# #
# This is not meant to replace Travis or other CI on the repo server, but # This is not meant to replace any CI on the repo server, but is a quick
# is a quick test to use locally during development. # test to use locally during development.
# #
COMPILERS="g++-5 g++-6 g++-7 g++-8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8" COMPILERS="g++-9 g++-11 g++-13 clang++-14 clang++-17 clang++-20"
[ "$#" -gt 0 ] && COMPILERS="$@" [ "$#" -gt 0 ] && COMPILERS="$@"
[ -z "${BUILD_JOBS}" ] && BUILD_JOBS=4 [ -z "${BUILD_JOBS}" ] && BUILD_JOBS=4
[ -n "${PAHO_C_PATH}" ] && PAHO_C_SWITCH="-DCMAKE_PREFIX_PATH=${PAHO_C_PATH}"
for COMPILER in $COMPILERS; do for COMPILER in $COMPILERS; do
if [ -z "$(which ${COMPILER})" ]; then if [ -z "$(which ${COMPILER})" ]; then
@ -25,30 +27,25 @@ for COMPILER in $COMPILERS; do
else else
printf "===== Testing: %s =====\n\n" "${COMPILER}" printf "===== Testing: %s =====\n\n" "${COMPILER}"
rm -rf buildtst-build/ rm -rf buildtst-build/
mkdir buildtst-build ; pushd buildtst-build &> /dev/null mkdir buildtst-build
pushd buildtst-build &> /dev/null
if ! cmake -DCMAKE_CXX_COMPILER=${COMPILER} -DPAHO_WITH_SSL=ON -DPAHO_BUILD_SAMPLES=ON -DPAHO_BUILD_TESTS=ON ${PAHO_C_SWITCH} .. ; then if ! cmake .. -DCMAKE_CXX_COMPILER=${COMPILER} -DPAHO_WITH_SSL=ON -DPAHO_BUILD_SAMPLES=ON -DPAHO_BUILD_TESTS=ON -DPAHO_WITH_MQTT_C=ON ; then
printf "\nCMake configuration failed for %s\n" "${COMPILER}" printf "\nCMake configuration failed for %s\n" "${COMPILER}"
exit 1 exit 1
fi fi
if ! make -j${BUILD_JOBS} ; then if ! cmake --build . -j ${BUILD_JOBS} ; then
printf "\nCompilation failed for %s\n" "${COMPILER}" printf "\nBuild failed for %s\n" "${COMPILER}"
exit 2 exit 2
fi fi
printf "Running Catch2 Unit tests for %s:\n" "${COMPILER}" printf "\nRunning Catch2 Unit tests for %s:\n" "${COMPILER}"
if ! ./test/unit/unit_tests ; then if ! ./test/unit/unit_tests ; then
printf "\nCatch2 unit test failed for %s\n" "${COMPILER}" printf "\nCatch2 unit test failed for %s\n" "${COMPILER}"
exit 3 exit 3
fi fi
printf "Running CppUnit tests for %s:\n" "${COMPILER}"
if ! ./test/cppunit/paho-mqttpp-test ; then
printf "\nUnit test failed for %s\n" "${COMPILER}"
exit 4
fi
popd &> /dev/null popd &> /dev/null
fi fi
printf "\n" printf "\n"
@ -57,7 +54,7 @@ done
rm -rf buildtst-build/ rm -rf buildtst-build/
printf "\nAll builds completed successfully\n\n" printf "\nAll builds completed successfully\n\n"
if ! cppcheck --enable=all --std=c++11 --force --quiet src/*.cpp ; then if ! cppcheck --enable=all --std=c++17 --force --quiet src/*.cpp ; then
printf "\ncppcheck failed\n" printf "\ncppcheck failed\n"
exit 5 exit 5
fi fi

View File

@ -1,22 +1,46 @@
# CMakeLists.txt
#
# CMake export file for the Paho C++ library.
#
#*******************************************************************************
# This is part of the Paho MQTT C++ client library.
#
# Copyright (c) 2017-2023, Frank Pagliughi
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v2.0
# and Eclipse Distribution License v1.0 which accompany this distribution.
#
# The Eclipse Public License is available at
# http://www.eclipse.org/legal/epl-v20.html
# and the Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#*******************************************************************************/
set(package_name PahoMqttCpp) set(package_name PahoMqttCpp)
configure_file(${package_name}Config.cmake.in ${package_name}Config.cmake @ONLY) configure_file(${package_name}Config.cmake.in ${package_name}Config.cmake @ONLY)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
write_basic_package_version_file( write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion) # TODO COMPATIBILITY SameMajorVersion
)
export(EXPORT ${package_name} export(EXPORT ${package_name}
FILE "${CMAKE_CURRENT_BINARY_DIR}/${package_name}Targets.cmake" FILE "${CMAKE_CURRENT_BINARY_DIR}/${package_name}Targets.cmake"
NAMESPACE ${package_name}::) NAMESPACE ${package_name}::
)
install(EXPORT ${package_name} DESTINATION lib/cmake/${package_name} install(EXPORT ${package_name}
FILE ${package_name}Targets.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}
NAMESPACE ${package_name}::) FILE ${package_name}Targets.cmake
NAMESPACE ${package_name}::
)
install(FILES install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${package_name}Config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${package_name}Config.cmake"
FindPahoMqttC.cmake "${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}
DESTINATION lib/cmake/${package_name}) )

View File

@ -0,0 +1,10 @@
if(CPACK_GENERATOR MATCHES "DEB")
set(CPACK_PACKAGE_NAME "libpaho-mqtt.cpp")
set(CPACK_DEBIAN_PACKAGE_NAME ${CPACK_PACKAGE_NAME})
set(CPACK_PACKAGE_CONTACT "Eclipse")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Eclipse Paho MQTT C++ client")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER " <>")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_PACKAGE_VERSION ${PACKAGE_VERSION})
set(CPACK_DEBIAN_PACKAGE_SECTION "net")
endif()

View File

@ -1,31 +0,0 @@
# find the Paho MQTT C library
if(PAHO_WITH_SSL)
set(_PAHO_MQTT_C_LIB_NAME paho-mqtt3as)
find_package(OpenSSL REQUIRED)
else()
set(_PAHO_MQTT_C_LIB_NAME paho-mqtt3a)
endif()
# add suffix when using static Paho MQTT C library variant
if(PAHO_BUILD_STATIC)
set(_PAHO_MQTT_C_LIB_NAME ${_PAHO_MQTT_C_LIB_NAME}-static)
endif()
find_library(PAHO_MQTT_C_LIBRARIES NAMES ${_PAHO_MQTT_C_LIB_NAME})
unset(_PAHO_MQTT_C_LIB_NAME)
find_path(PAHO_MQTT_C_INCLUDE_DIRS NAMES MQTTAsync.h)
add_library(PahoMqttC::PahoMqttC UNKNOWN IMPORTED)
set_target_properties(PahoMqttC::PahoMqttC PROPERTIES
IMPORTED_LOCATION "${PAHO_MQTT_C_LIBRARIES}"
INTERFACE_INCLUDE_DIRECTORIES "${PAHO_MQTT_C_INCLUDE_DIRS}"
IMPORTED_LINK_INTERFACE_LANGUAGES "C")
if(PAHO_WITH_SSL)
set_target_properties(PahoMqttC::PahoMqttC PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "OPENSSL=1"
INTERFACE_LINK_LIBRARIES "OpenSSL::SSL;OpenSSL::Crypto")
endif()
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(PahoMqttC
REQUIRED_VARS PAHO_MQTT_C_LIBRARIES PAHO_MQTT_C_INCLUDE_DIRS)

View File

@ -2,10 +2,26 @@
set(PAHO_BUILD_STATIC @PAHO_BUILD_STATIC@) set(PAHO_BUILD_STATIC @PAHO_BUILD_STATIC@)
set(PAHO_BUILD_SHARED @PAHO_BUILD_SHARED@) set(PAHO_BUILD_SHARED @PAHO_BUILD_SHARED@)
set(PAHO_WITH_SSL @PAHO_WITH_SSL@) set(PAHO_WITH_SSL @PAHO_WITH_SSL@)
set(PAHO_WITH_MQTT_C @PAHO_WITH_MQTT_C@)
include(CMakeFindDependencyMacro) include(CMakeFindDependencyMacro)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
find_dependency(PahoMqttC REQUIRED)
list(REMOVE_AT CMAKE_MODULE_PATH -1)
include("${CMAKE_CURRENT_LIST_DIR}/@package_name@Targets.cmake") find_dependency(Threads REQUIRED)
if (NOT PAHO_WITH_MQTT_C)
find_dependency(eclipse-paho-mqtt-c REQUIRED)
endif()
if (PAHO_WITH_SSL)
find_dependency(OpenSSL REQUIRED)
endif()
if(NOT TARGET PahoMqttCpp::paho-mqttpp3-shared AND NOT TARGET PahoMqttCpp::paho-mqttpp3-static)
include("${CMAKE_CURRENT_LIST_DIR}/@package_name@Targets.cmake")
if(TARGET PahoMqttCpp::paho-mqttpp3-shared)
add_library(PahoMqttCpp::paho-mqttpp3 ALIAS PahoMqttCpp::paho-mqttpp3-shared)
else()
add_library(PahoMqttCpp::paho-mqttpp3 ALIAS PahoMqttCpp::paho-mqttpp3-static)
endif()
endif()

10
dist/paho-cpp.spec vendored
View File

@ -1,8 +1,8 @@
Summary: MQTT CPP Client Summary: MQTT CPP Client
Name: paho-cpp Name: paho-cpp
Version: 1.0.0 Version: 1.5.1
Release: 0%{?dist} Release: 0%{?dist}
License: Eclipse Distribution License 1.0 and Eclipse Public License 1.0 License: Eclipse Eclipse Public License 2.0 and Distribution License 1.0
Group: Development/Tools Group: Development/Tools
Source: https://github.com/eclipse/paho.mqtt.cpp/archive/v%{version}.tar.gz Source: https://github.com/eclipse/paho.mqtt.cpp/archive/v%{version}.tar.gz
URL: https://eclipse.org/paho/clients/cpp/ URL: https://eclipse.org/paho/clients/cpp/
@ -17,7 +17,7 @@ Requires: paho-c
%description %description
The Paho MQTT CPP Client is a fully fledged MQTT client written in ANSI standard C++ 11. The Paho MQTT CPP Client is a fully fledged MQTT client written in ANSI standard C++ 17.
%package devel %package devel
@ -41,7 +41,7 @@ Development documentation files for the the Paho MQTT CPP Client.
%build %build
mkdir build.paho.cpp && cd build.paho.cpp mkdir build.paho.cpp && cd build.paho.cpp
%cmake3 -DPAHO_WITH_SSL=TRUE -DPAHO_BUILD_DOCUMENTATION=TRUE -DPAHO_BUILD_SAMPLES=TRUE .. %cmake3 -DPAHO_WITH_SSL=TRUE -DPAHO_BUILD_DOCUMENTATION=TRUE -DPAHO_BUILD_EXAMPLES=TRUE ..
make %{?_smp_mflags} make %{?_smp_mflags}
%install %install
@ -49,7 +49,7 @@ cd build.paho.cpp
make install DESTDIR=%{buildroot} make install DESTDIR=%{buildroot}
%files %files
%doc edl-v10 epl-v10 %doc edl-v10 epl-v20
%{_libdir}/* %{_libdir}/*
%files devel %files devel

View File

@ -1,43 +1,47 @@
#******************************************************************************* #*******************************************************************************
# Copyright (c) 2016 # Copyright (c) 2024, Frank Pagliughi
# Copyright (c) 2016, Guilherme Maciel Ferreira
# #
# All rights reserved. This program and the accompanying materials # All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0 # are made available under the terms of the Eclipse Public License v2.0
# and Eclipse Distribution License v1.0 which accompany this distribution. # and Eclipse Distribution License v1.0 which accompany this distribution.
# #
# The Eclipse Public License is available at # The Eclipse Public License is available at
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v20.html
# and the Eclipse Distribution License is available at # and the Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php. # http://www.eclipse.org/org/documents/edl-v10.php.
# #
# Contributors: # Contributors:
# Frank Pagliughi - Updated paths. Fixed conflict with Paho C
# Guilherme Maciel Ferreira - initial version # Guilherme Maciel Ferreira - initial version
#*******************************************************************************/ #*******************************************************************************/
## documentation settings ## documentation settings
find_package(Doxygen) find_package(Doxygen REQUIRED)
if(NOT DOXYGEN_FOUND)
message(FATAL_ERROR "Doxygen is needed to build the documentation.")
endif()
set(DOXYTARGETS)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc)
set(DOXYFILE_SRC Doxyfile.cmake) message(STATUS "Doxygen: ${DOXYGEN_EXECUTABLE}")
set(DOXYFILE ${CMAKE_CURRENT_BINARY_DIR}/${DOXYFILE_SRC})
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
@ONLY
)
configure_file(${DOXYFILE_SRC} ${DOXYFILE} @ONLY)
add_custom_target( add_custom_target(
${DOXYFILE_SRC}.target paho-mqttpp3-doc.target
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE} COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen" COMMENT "Generating API documentation with Doxygen"
VERBATIM VERBATIM
) )
set(DOXYTARGETS ${DOXYTARGETS} ${DOXYFILE_SRC}.target)
add_custom_target(doc ALL DEPENDS ${DOXYTARGETS}) add_custom_target(paho-mqttpp3-doc ALL
DEPENDS paho-mqttpp3-doc.target
)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc DESTINATION share) install(
DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc/
DESTINATION share/doc/EclipsePahoCpp
)

View File

@ -648,8 +648,8 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories # directories like "/usr/src/myproject". Separate the files or directories
# with spaces. # with spaces.
STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@/src/mqtt STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@/include/mqtt
INPUT = @PROJECT_SOURCE_DIR@/src/mqtt/ INPUT = @PROJECT_SOURCE_DIR@/include/mqtt/
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

70
epl-v10
View File

@ -1,70 +0,0 @@
Eclipse Public License - v 1.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
"Contributor" means any person or entity that distributes the Program.
"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
"Program" means the Contributions distributed in accordance with this Agreement.
"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
a) it complies with the terms and conditions of this Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each copy of the Program.
Contributors may not remove or alter any copyright notices contained within the Program.
Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.

277
epl-v20 Normal file
View File

@ -0,0 +1,277 @@
Eclipse Public License - v 2.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial content
Distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from
and are Distributed by that particular Contributor. A Contribution
"originates" from a Contributor if it was added to the Program by
such Contributor itself or anyone acting on such Contributor's behalf.
Contributions do not include changes or additions to the Program that
are not Modified Works.
"Contributor" means any person or entity that Distributes the Program.
"Licensed Patents" mean patent claims licensable by a Contributor which
are necessarily infringed by the use or sale of its Contribution alone
or when combined with the Program.
"Program" means the Contributions Distributed in accordance with this
Agreement.
"Recipient" means anyone who receives the Program under this Agreement
or any Secondary License (as applicable), including Contributors.
"Derivative Works" shall mean any work, whether in Source Code or other
form, that is based on (or derived from) the Program and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship.
"Modified Works" shall mean any work in Source Code or other form that
results from an addition to, deletion from, or modification of the
contents of the Program, including, for purposes of clarity any new file
in Source Code form that contains any contents of the Program. Modified
Works shall not include works that contain only declarations,
interfaces, types, classes, structures, or files of the Program solely
in each case in order to link to, bind by name, or subclass the Program
or Modified Works thereof.
"Distribute" means the acts of a) distributing or b) making available
in any manner that enables the transfer of a copy.
"Source Code" means the form of a Program preferred for making
modifications, including but not limited to software source code,
documentation source, and configuration files.
"Secondary License" means either the GNU General Public License,
Version 2.0, or any later versions of that license, including any
exceptions or additional permissions as identified by the initial
Contributor.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free copyright
license to reproduce, prepare Derivative Works of, publicly display,
publicly perform, Distribute and sublicense the Contribution of such
Contributor, if any, and such Derivative Works.
b) Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free patent
license under Licensed Patents to make, use, sell, offer to sell,
import and otherwise transfer the Contribution of such Contributor,
if any, in Source Code or other form. This patent license shall
apply to the combination of the Contribution and the Program if, at
the time the Contribution is added by the Contributor, such addition
of the Contribution causes such combination to be covered by the
Licensed Patents. The patent license shall not apply to any other
combinations which include the Contribution. No hardware per se is
licensed hereunder.
c) Recipient understands that although each Contributor grants the
licenses to its Contributions set forth herein, no assurances are
provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity.
Each Contributor disclaims any liability to Recipient for claims
brought by any other entity based on infringement of intellectual
property rights or otherwise. As a condition to exercising the
rights and licenses granted hereunder, each Recipient hereby
assumes sole responsibility to secure any other intellectual
property rights needed, if any. For example, if a third party
patent license is required to allow Recipient to Distribute the
Program, it is Recipient's responsibility to acquire that license
before distributing the Program.
d) Each Contributor represents that to its knowledge it has
sufficient copyright rights in its Contribution, if any, to grant
the copyright license set forth in this Agreement.
e) Notwithstanding the terms of any Secondary License, no
Contributor makes additional grants to any Recipient (other than
those set forth in this Agreement) as a result of such Recipient's
receipt of the Program under the terms of a Secondary License
(if permitted under the terms of Section 3).
3. REQUIREMENTS
3.1 If a Contributor Distributes the Program in any form, then:
a) the Program must also be made available as Source Code, in
accordance with section 3.2, and the Contributor must accompany
the Program with a statement that the Source Code for the Program
is available under this Agreement, and informs Recipients how to
obtain it in a reasonable manner on or through a medium customarily
used for software exchange; and
b) the Contributor may Distribute the Program under a license
different than this Agreement, provided that such license:
i) effectively disclaims on behalf of all other Contributors all
warranties and conditions, express and implied, including
warranties or conditions of title and non-infringement, and
implied warranties or conditions of merchantability and fitness
for a particular purpose;
ii) effectively excludes on behalf of all other Contributors all
liability for damages, including direct, indirect, special,
incidental and consequential damages, such as lost profits;
iii) does not attempt to limit or alter the recipients' rights
in the Source Code under section 3.2; and
iv) requires any subsequent distribution of the Program by any
party to be under a license that satisfies the requirements
of this section 3.
3.2 When the Program is Distributed as Source Code:
a) it must be made available under this Agreement, or if the
Program (i) is combined with other material in a separate file or
files made available under a Secondary License, and (ii) the initial
Contributor attached to the Source Code the notice described in
Exhibit A of this Agreement, then the Program may be made available
under the terms of such Secondary Licenses, and
b) a copy of this Agreement must be included with each copy of
the Program.
3.3 Contributors may not remove or alter any copyright, patent,
trademark, attribution notices, disclaimers of warranty, or limitations
of liability ("notices") contained within the Program from any copy of
the Program which they Distribute, provided that Contributors may add
their own appropriate notices.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities
with respect to end users, business partners and the like. While this
license is intended to facilitate the commercial use of the Program,
the Contributor who includes the Program in a commercial product
offering should do so in a manner which does not create potential
liability for other Contributors. Therefore, if a Contributor includes
the Program in a commercial product offering, such Contributor
("Commercial Contributor") hereby agrees to defend and indemnify every
other Contributor ("Indemnified Contributor") against any losses,
damages and costs (collectively "Losses") arising from claims, lawsuits
and other legal actions brought by a third party against the Indemnified
Contributor to the extent caused by the acts or omissions of such
Commercial Contributor in connection with its distribution of the Program
in a commercial product offering. The obligations in this section do not
apply to any claims or Losses relating to any actual or alleged
intellectual property infringement. In order to qualify, an Indemnified
Contributor must: a) promptly notify the Commercial Contributor in
writing of such claim, and b) allow the Commercial Contributor to control,
and cooperate with the Commercial Contributor in, the defense and any
related settlement negotiations. The Indemnified Contributor may
participate in any such claim at its own expense.
For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those performance
claims and warranties, and if a court requires any other Contributor to
pay any damages as a result, the Commercial Contributor must pay
those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
PURPOSE. Each Recipient is solely responsible for determining the
appropriateness of using and distributing the Program and assumes all
risks associated with its exercise of rights under this Agreement,
including but not limited to the risks and costs of program errors,
compliance with applicable laws, damage to or loss of data, programs
or equipment, and unavailability or interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further
action by the parties hereto, such provision shall be reformed to the
minimum extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other software
or hardware) infringes such Recipient's patent(s), then such Recipient's
rights granted under Section 2(b) shall terminate as of the date such
litigation is filed.
All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of
time after becoming aware of such noncompliance. If all Recipient's
rights under this Agreement terminate, Recipient agrees to cease use
and distribution of the Program as soon as reasonably practicable.
However, Recipient's obligations under this Agreement and any licenses
granted by Recipient relating to the Program shall continue and survive.
Everyone is permitted to copy and distribute copies of this Agreement,
but in order to avoid inconsistency the Agreement is copyrighted and
may only be modified in the following manner. The Agreement Steward
reserves the right to publish new versions (including revisions) of
this Agreement from time to time. No one other than the Agreement
Steward has the right to modify this Agreement. The Eclipse Foundation
is the initial Agreement Steward. The Eclipse Foundation may assign the
responsibility to serve as the Agreement Steward to a suitable separate
entity. Each new version of the Agreement will be given a distinguishing
version number. The Program (including Contributions) may always be
Distributed subject to the version of the Agreement under which it was
received. In addition, after a new version of the Agreement is published,
Contributor may elect to Distribute the Program (including its
Contributions) under the new version.
Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
receives no rights or licenses to the intellectual property of any
Contributor under this Agreement, whether expressly, by implication,
estoppel or otherwise. All rights in the Program not expressly granted
under this Agreement are reserved. Nothing in this Agreement is intended
to be enforceable by any entity that is not a Contributor or Recipient.
No third-party beneficiary rights are created under this Agreement.
Exhibit A - Form of Secondary Licenses Notice
"This Source Code may also be made available under the following
Secondary Licenses when the conditions for such availability set forth
in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
version(s), and exceptions or additional permissions here}."
Simply including a copy of this Agreement, including this Exhibit A
is not sufficient to license the Source Code under Secondary Licenses.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to
look for such a notice.
You may add additional accurate notices of copyright ownership.

88
examples/CMakeLists.txt Normal file
View File

@ -0,0 +1,88 @@
# CMakeLists.txt
#
# CMake file for the Paho C++ example applications.
#
#*******************************************************************************
# This is part of the Paho MQTT C++ client library.
#
# Copyright (c) 2016-2024
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v2.0
# and Eclipse Distribution License v1.0 which accompany this distribution.
#
# The Eclipse Public License is available at
# http://www.eclipse.org/legal/epl-v20.html
# and the Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Guilherme Maciel Ferreira - initial version
# Frank Pagliughi - Updates for new samples
#*******************************************************************************/
## --- Library dependencies ---
set (THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# The example applications
set(EXECUTABLES
async_publish
async_publish_time
async_subscribe
async_subscribe_v5
async_consume
async_consume_v5
async_message_consume
async_message_consume_v5
data_publish
mqttpp_chat
multithr_pub_sub
pub_speed_test
rpc_math_cli
rpc_math_srvr
server_props_v5
sync_publish
sync_consume
sync_consume_v5
sync_reconnect
topic_publish
ws_publish
)
# These will only be built if SSL selected
if(PAHO_WITH_SSL)
set(SSL_EXECUTABLES ssl_publish)
endif()
## Build the example apps
foreach(EXECUTABLE ${EXECUTABLES} ${SSL_EXECUTABLES})
add_executable(${EXECUTABLE} ${EXECUTABLE}.cpp)
target_link_libraries(${EXECUTABLE} PahoMqttCpp::paho-mqttpp3)
set_target_properties(${EXECUTABLE} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)
if(PAHO_BUILD_SHARED)
target_compile_definitions(${EXECUTABLE} PRIVATE PAHO_MQTTPP_IMPORTS)
endif()
endforeach()
## Extra configuration for the SSL/TLS examples, if selected
foreach(EXECUTABLE ${SSL_EXECUTABLES})
target_compile_definitions(${EXECUTABLE} PUBLIC OPENSSL)
endforeach()
## install binaries
include(GNUInstallDirs)
install(TARGETS ${EXECUTABLES} ${SSL_EXECUTABLES}
EXPORT PahoMqttCppSamples
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

115
examples/async_consume.cpp Normal file
View File

@ -0,0 +1,115 @@
// async_consume.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT consumer/subscriber using the C++
// asynchronous client interface, employing the to receive messages
// and status updates.
//
// The sample demonstrates:
// - Connecting to an MQTT v3 server/broker.
// - Subscribing to a topic
// - Persistent subscriber session
// - Receiving messages through the synchronous queuing API
// - Auto reconnecting
//
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
using namespace std;
const string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const string CLIENT_ID{"paho_cpp_async_consume"};
const string TOPIC{"hello"};
const int QOS = 1;
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
auto serverUri = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI;
mqtt::async_client cli(serverUri, CLIENT_ID);
auto connOpts = mqtt::connect_options_builder::v3()
.keep_alive_interval(30s)
.clean_session(false)
.automatic_reconnect()
.finalize();
try {
// Start consumer before connecting to make sure to not miss any messages
cli.start_consuming();
// Connect to the server
cout << "Connecting to the MQTT server..." << flush;
auto tok = cli.connect(connOpts);
// Getting the connect response will block waiting for the
// connection to complete.
auto rsp = tok->get_connect_response();
// If there is no session present, then we need to subscribe, but if
// there is a session, then the server remembers us and our
// subscriptions.
if (!rsp.is_session_present()) {
cout << " No session present on server. Subscribing..." << flush;
cli.subscribe(TOPIC, QOS)->wait();
}
cout << "OK" << endl;
// Consume messages
cout << "\nWaiting for messages on topic: '" << TOPIC << "'" << endl;
// The client handles automatic reconnects, but we monitor
// the events here to report them to the user.
while (true) {
auto evt = cli.consume_event();
if (const auto* p = evt.get_message_if()) {
auto& msg = *p;
if (msg)
cout << msg->get_topic() << ": " << msg->to_string() << endl;
}
else if (evt.is_connected())
cout << "\n*** Connected ***" << endl;
else if (evt.is_connection_lost())
cout << "*** Connection Lost ***" << endl;
}
}
catch (const mqtt::exception& exc) {
cerr << "\n " << exc << endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,160 @@
// async_consume_v5.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT consumer/subscriber using the C++
// asynchronous client interface, employing the to receive messages
// and status updates.
//
// The sample demonstrates:
// - Connecting to an MQTT v5 server/broker.
// - Subscribing to a topic
// - Receiving messages through the consuming (queuing) API
//
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
using namespace std;
const string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const string CLIENT_ID{"PahoCppAsyncConsumeV5"};
// const string TOPIC{"hello"};
const string TOPIC{"#"};
const int QOS = 1;
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
auto serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI;
auto cli = std::make_shared<mqtt::async_client>(serverURI, CLIENT_ID);
auto connOpts = mqtt::connect_options_builder::v5()
.clean_start(false)
.properties({{mqtt::property::SESSION_EXPIRY_INTERVAL, 604800}})
.finalize();
try {
// Start consumer before connecting to make sure to not miss messages
cli->start_consuming();
// Connect to the server
cout << "Connecting to the MQTT server..." << flush;
auto tok = cli->connect(connOpts);
// Getting the connect response will block waiting for the
// connection to complete.
auto rsp = tok->get_connect_response();
// Make sure we were granted a v5 connection.
if (rsp.get_mqtt_version() < MQTTVERSION_5) {
cout << "\n Did not get an MQTT v5 connection." << flush;
exit(1);
}
// If there is no session present, then we need to subscribe, but if
// there is a session, then the server remembers us and our
// subscriptions.
if (!rsp.is_session_present()) {
cout << "\n Session not present on broker. Subscribing..." << flush;
cli->subscribe(TOPIC, QOS)->wait();
}
cout << "\n OK" << endl;
// We'll signal the consumer to exit from another thread.
// (just to show that we can)
thread([cli] {
this_thread::sleep_for(60s);
cout << "\nClosing the consumer." << endl;
cli->stop_consuming();
}).detach();
// Consume messages
//
// This just exits if the consumer is closed or the client is
// disconnected. (See some other examples for auto or manual
// reconnect)
cout << "\nWaiting for messages on topic: '" << TOPIC << "'" << endl;
try {
while (true) {
auto evt = cli->consume_event();
if (const auto* p = evt.get_message_if()) {
auto& msg = *p;
if (!msg)
continue;
cout << msg->get_topic() << ": " << msg->to_string();
const auto& props = msg->get_properties();
if (size_t n = props.size(); n != 0) {
cout << "\n [";
for (size_t i = 0; i < n - 1; ++i) cout << props[i] << ", ";
cout << props[n - 1] << "]";
}
cout << endl;
}
else if (evt.is_connected()) {
cout << "\n*** Connected ***" << endl;
}
else if (evt.is_connection_lost()) {
cout << "*** Connection Lost ***" << endl;
break;
}
else if (const auto* p = evt.get_disconnected_if()) {
cout << "*** Disconnected. Reason [0x" << hex << int{p->reasonCode}
<< "]: " << p->reasonCode << " ***" << endl;
break;
}
}
}
catch (mqtt::queue_closed&) {
}
// If we're here, the client was almost certainly disconnected.
// But we check, just to make sure.
if (cli->is_connected()) {
cout << "\nShutting down and disconnecting from the MQTT server..." << flush;
cli->disconnect()->wait();
cout << "OK" << endl;
}
}
catch (const mqtt::exception& exc) {
cerr << "\n " << exc << endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,114 @@
// async_consume.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT consumer/subscriber using the C++
// asynchronous client interface, employing the to receive messages
// and status updates.
//
// The sample demonstrates:
// - Connecting to an MQTT v3 server/broker.
// - Subscribing to a topic
// - Persistent subscriber session
// - Receiving messages through the synchronous queuing API
// - Auto reconnecting
//
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
using namespace std;
const string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const string CLIENT_ID{"paho_cpp_async_consume"};
const string TOPIC{"hello"};
const int QOS = 1;
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
auto serverUri = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI;
mqtt::async_client cli(serverUri, CLIENT_ID);
auto connOpts = mqtt::connect_options_builder::v3()
.keep_alive_interval(30s)
.clean_session(false)
.automatic_reconnect()
.finalize();
// The client will handle automatic reconnects, but we add this
// callbacks to let the user know when we're reconnected.
cli.set_connected_handler([](const std::string&) {
cout << "\n*** Connected ***" << endl;
});
try {
// Start consumer before connecting to make sure to not miss any messages
cli.start_consuming();
// Connect to the server
cout << "Connecting to the MQTT server..." << flush;
auto tok = cli.connect(connOpts);
// Getting the connect response will block waiting for the
// connection to complete.
auto rsp = tok->get_connect_response();
// If there is no session present, then we need to subscribe, but if
// there is a session, then the server remembers us and our
// subscriptions.
if (!rsp.is_session_present()) {
cout << " No session present on server. Subscribing..." << flush;
cli.subscribe(TOPIC, QOS)->wait();
}
cout << "OK" << endl;
// Consume messages
cout << "\nWaiting for messages on topic: '" << TOPIC << "'" << endl;
while (true) {
auto msg = cli.consume_message();
if (msg)
cout << msg->get_topic() << ": " << msg->to_string() << endl;
else
cout << "*** Connection Lost ***" << endl;
}
}
catch (const mqtt::exception& exc) {
cerr << "\n " << exc << endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,133 @@
// async_consume_v5.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT consumer/subscriber using the C++
// asynchronous client interface, employing the to receive messages
// and status updates.
//
// The sample demonstrates:
// - Connecting to an MQTT v5 server/broker.
// - Subscribing to a topic
// - Receiving messages through the consuming (queuing) API
//
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
using namespace std;
const string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const string CLIENT_ID{"PahoCppAsyncConsumeV5"};
const string TOPIC{"hello"};
const int QOS = 1;
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
auto serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI;
mqtt::async_client cli(serverURI, CLIENT_ID);
auto connOpts = mqtt::connect_options_builder::v5()
.clean_start(false)
.properties({{mqtt::property::SESSION_EXPIRY_INTERVAL, 604800}})
.finalize();
try {
cli.set_connection_lost_handler([](const std::string&) {
cout << "*** Connection Lost ***" << endl;
});
cli.set_disconnected_handler([](const mqtt::properties&, mqtt::ReasonCode reason) {
cout << "*** Disconnected. Reason [0x" << hex << int{reason} << "]: " << reason
<< " ***" << endl;
});
// Start consumer before connecting to make sure to not miss messages
cli.start_consuming();
// Connect to the server
cout << "Connecting to the MQTT server..." << flush;
auto tok = cli.connect(connOpts);
// Getting the connect response will block waiting for the
// connection to complete.
auto rsp = tok->get_connect_response();
// Make sure we were granted a v5 connection.
if (rsp.get_mqtt_version() < MQTTVERSION_5) {
cout << "\n Did not get an MQTT v5 connection." << flush;
exit(1);
}
// If there is no session present, then we need to subscribe, but if
// there is a session, then the server remembers us and our
// subscriptions.
if (!rsp.is_session_present()) {
cout << "\n Session not present on broker. Subscribing..." << flush;
cli.subscribe(TOPIC, QOS)->wait();
}
cout << "\n OK" << endl;
// Consume messages
// This just exits if the client is disconnected.
// (See some other examples for auto or manual reconnect)
cout << "\nWaiting for messages on topic: '" << TOPIC << "'" << endl;
while (true) {
auto msg = cli.consume_message();
if (!msg)
break;
cout << msg->get_topic() << ": " << msg->to_string() << endl;
}
// If we're here, the client was almost certainly disconnected.
// But we check, just to make sure.
if (cli.is_connected()) {
cout << "\nShutting down and disconnecting from the MQTT server..." << flush;
cli.stop_consuming();
cli.disconnect()->wait();
cout << "OK" << endl;
}
else {
cout << "\nClient was disconnected" << endl;
}
}
catch (const mqtt::exception& exc) {
cerr << "\n " << exc << endl;
return 1;
}
return 0;
}

221
examples/async_publish.cpp Normal file
View File

@ -0,0 +1,221 @@
// async_publish.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// It's an example of how to send messages as an MQTT publisher using the
// C++ asynchronous client interface.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker
// - Using a connect timeout
// - Publishing messages
// - Default file persistence
// - Last will and testament
// - Using asynchronous tokens
// - Implementing callbacks and action listeners
//
/*******************************************************************************
* Copyright (c) 2013-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
using namespace std;
using namespace std::chrono;
const string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const string CLIENT_ID{"paho_cpp_async_publish"};
const mqtt::persistence_type PERSIST_DIR{"./persist"};
const string TOPIC{"hello"};
const string PAYLOAD1{"Hello World!"};
const string PAYLOAD2{"Hi there!"};
const string PAYLOAD3{"Is anyone listening?"};
const string PAYLOAD4{"Someone is always listening."};
const string LWT_PAYLOAD{"Last will and testament."};
const int QOS = 1;
const auto TIMEOUT = std::chrono::seconds(10);
/////////////////////////////////////////////////////////////////////////////
/**
* A callback class for use with the main MQTT client.
*/
class callback : public virtual mqtt::callback
{
public:
void connection_lost(const string& cause) override
{
cout << "\nConnection lost" << endl;
if (!cause.empty())
cout << "\tcause: " << cause << endl;
}
void delivery_complete(mqtt::delivery_token_ptr tok) override
{
cout << "\tDelivery complete for token: " << (tok ? tok->get_message_id() : -1)
<< endl;
}
};
/////////////////////////////////////////////////////////////////////////////
/**
* A base action listener.
*/
class action_listener : public virtual mqtt::iaction_listener
{
protected:
void on_failure(const mqtt::token& tok) override
{
cout << "\tListener failure for token: " << tok.get_message_id() << endl;
}
void on_success(const mqtt::token& tok) override
{
cout << "\tListener success for token: " << tok.get_message_id() << endl;
}
};
/////////////////////////////////////////////////////////////////////////////
/**
* A derived action listener for publish events.
*/
class delivery_action_listener : public action_listener
{
atomic<bool> done_;
void on_failure(const mqtt::token& tok) override
{
action_listener::on_failure(tok);
done_ = true;
}
void on_success(const mqtt::token& tok) override
{
action_listener::on_success(tok);
done_ = true;
}
public:
delivery_action_listener() : done_(false) {}
bool is_done() const { return done_; }
};
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
// A client that just publishes normally doesn't need a persistent
// session or Client ID unless it's using persistence, then the local
// library requires an ID to identify the persistence files.
string serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI,
clientID = (argc > 2) ? string{argv[2]} : CLIENT_ID;
cout << "Initializing for server '" << serverURI << "'..." << endl;
mqtt::async_client client(serverURI, clientID, PERSIST_DIR);
callback cb;
client.set_callback(cb);
auto connOpts = mqtt::connect_options_builder()
.connect_timeout(5s)
.clean_session()
.will(mqtt::message(TOPIC, LWT_PAYLOAD, QOS, false))
.finalize();
cout << " ...OK" << endl;
try {
cout << "\nConnecting..." << endl;
mqtt::token_ptr conntok = client.connect(connOpts);
cout << "Waiting for the connection..." << endl;
conntok->wait();
cout << " ...OK" << endl;
// First use a message pointer.
cout << "\nSending message..." << endl;
mqtt::message_ptr pubmsg = mqtt::make_message(TOPIC, PAYLOAD1);
pubmsg->set_qos(QOS);
client.publish(pubmsg)->wait_for(TIMEOUT);
cout << " ...OK" << endl;
// Now try with itemized publish.
cout << "\nSending next message..." << endl;
mqtt::delivery_token_ptr pubtok;
pubtok = client.publish(TOPIC, PAYLOAD2, QOS, false);
cout << " ...with token: " << pubtok->get_message_id() << endl;
cout << " ...for message with " << pubtok->get_message()->get_payload().size()
<< " bytes" << endl;
pubtok->wait_for(TIMEOUT);
cout << " ...OK" << endl;
// Now try with a listener
cout << "\nSending next message..." << endl;
action_listener listener;
pubmsg = mqtt::make_message(TOPIC, PAYLOAD3);
pubtok = client.publish(pubmsg, nullptr, listener);
pubtok->wait();
cout << " ...OK" << endl;
// Finally try with a listener, but no token
cout << "\nSending final message..." << endl;
delivery_action_listener deliveryListener;
pubmsg = mqtt::make_message(TOPIC, PAYLOAD4);
client.publish(pubmsg, nullptr, deliveryListener);
while (!deliveryListener.is_done()) {
this_thread::sleep_for(std::chrono::milliseconds(100));
}
cout << "OK" << endl;
// Double check that there are no pending tokens
auto toks = client.get_pending_delivery_tokens();
if (!toks.empty())
cout << "Error: There are pending delivery tokens!" << endl;
// Disconnect
cout << "\nDisconnecting..." << endl;
client.disconnect()->wait();
cout << " ...OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.what() << endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,173 @@
// async_publish_time.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// It's a fairly contrived, but useful example of an MQTT data monitor and
// publisher, using the C++ asynchronous client interface. A fairly common
// usage for MQTT applications to monitor a sensor and publish the reading
// when it changes by a "significant" amount (whatever that may be).
// This might be temperature, pressure, humidity, soil moisture, CO2 levels,
// or anything like that.
//
// Since we don't have a universal sensor to use for this example, we simply
// use time itself as out input data. We periodically "sample" the time
// value and when it changes by more than our required delta amount, we
// publish the time. In this case we use the system clock, measuring the
// time with millisecond precision.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker
// - Sampling a value
// - Publishing messages using a `topic` object
// - Last will and testament
// - Callbacks with lambdas (on connect and disconnect)
// - Using `create_options`
// - Creating options with builder classes
// - Offline buffering in the client
//
/*******************************************************************************
* Copyright (c) 2019-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <atomic>
#include <chrono>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread> // For sleep
#include "mqtt/async_client.h"
using namespace std;
using namespace std::chrono;
const std::string DFLT_SERVER_URI{"mqtt://localhost:1883"};
// The QoS for sending data
const int QOS = 1;
// How often to sample the "data"
const auto SAMPLE_PERIOD = 5ms;
// How much the "data" needs to change before we publish a new value.
const int DELTA_MS = 100;
// How many to buffer while off-line
const int MAX_BUFFERED_MESSAGES = 1200;
// Atomic flag to tell the main loop to exit.
atomic<bool> quit{false};
// Handler for ^C (SIGINT)
void ctrlc_handler(int) { quit = true; }
// --------------------------------------------------------------------------
// Gets the current time as the number of milliseconds since the epoch:
// like a time_t with ms resolution.
uint64_t timestamp()
{
auto now = system_clock::now();
auto tse = now.time_since_epoch();
auto msTm = duration_cast<milliseconds>(tse);
return uint64_t(msTm.count());
}
// --------------------------------------------------------------------------
int main(int argc, char* argv[])
{
// The server URI (address)
string serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI;
// The amount of time to run (in ms). Zero means "run forever".
uint64_t trun = (argc > 2) ? stoll(argv[2]) : 0LL;
cout << "Initializing for server '" << serverURI << "'..." << endl;
// We configure to allow publishing to the client while off-line,
// and that it's OK to do so before the 1st successful connection.
auto createOpts = mqtt::create_options_builder()
.server_uri(serverURI)
.send_while_disconnected(true, true)
.max_buffered_messages(MAX_BUFFERED_MESSAGES)
.delete_oldest_messages()
.finalize();
mqtt::async_client cli(createOpts);
// Set callbacks for when connected and connection lost.
cli.set_connected_handler([&cli](const std::string&) {
std::cout << "*** Connected (" << timestamp() << ") ***" << std::endl;
});
cli.set_connection_lost_handler([&cli](const std::string&) {
std::cout << "*** Connection Lost (" << timestamp() << ") ***" << std::endl;
});
auto willMsg = mqtt::message("test/events", "Time publisher disconnected", 1, true);
auto connOpts = mqtt::connect_options_builder()
.clean_session()
.will(willMsg)
.keep_alive_interval(10s)
.automatic_reconnect(seconds(1), seconds(10))
.finalize();
try {
// Note that we start the connection, but don't wait for completion.
// We configured to allow publishing before a successful connection.
cout << "Starting connection..." << endl;
cli.connect(connOpts);
auto top = mqtt::topic(cli, "data/time", QOS);
cout << "Publishing data..." << endl;
// Install a ^C handler for user to signal when to exit
signal(SIGINT, ctrlc_handler);
// Sync clock to start of delta period
while (timestamp() % DELTA_MS != 0);
uint64_t t = timestamp(), tlast = t, tstart = t;
top.publish(to_string(t));
while (!quit) {
this_thread::sleep_for(SAMPLE_PERIOD);
t = timestamp();
if (abs(int(t - tlast)) >= DELTA_MS)
top.publish(to_string(tlast = t));
if (trun > 0 && t >= (trun + tstart))
break;
}
// Disconnect
cout << "\nDisconnecting..." << endl;
cli.disconnect()->wait();
cout << " ...OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.what() << endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,226 @@
// async_subscribe.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT subscriber using the C++ asynchronous client
// interface, employing callbacks to receive messages and status updates.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker using MQTT v3.
// - Subscribing to a topic
// - Receiving messages through the callback API
// - Receiving network disconnect updates and attempting manual reconnects.
// - Using a "clean session" and manually re-subscribing to topics on
// reconnect.
//
/*******************************************************************************
* Copyright (c) 2013-2025 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
const std::string DFLT_SERVER_URI("mqtt://localhost:1883");
const std::string CLIENT_ID("paho_cpp_async_subscribe");
const std::string TOPIC("#");
const int QOS = 1;
const int N_RETRY_ATTEMPTS = 5;
/////////////////////////////////////////////////////////////////////////////
// Callbacks for the success or failures of requested actions.
// This could be used to initiate further action, but here we just log the
// results to the console.
class action_listener : public virtual mqtt::iaction_listener
{
std::string name_;
void on_failure(const mqtt::token& tok) override
{
std::cout << name_ << " failure";
if (tok.get_message_id() != 0)
std::cout << " for token: [" << tok.get_message_id() << "]" << std::endl;
std::cout << std::endl;
}
void on_success(const mqtt::token& tok) override
{
std::cout << name_ << " success";
if (tok.get_message_id() != 0)
std::cout << " for token: [" << tok.get_message_id() << "]" << std::endl;
auto top = tok.get_topics();
if (top && !top->empty())
std::cout << "\ttoken topic: '" << (*top)[0] << "', ..." << std::endl;
std::cout << std::endl;
}
public:
action_listener(const std::string& name) : name_(name) {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* Local callback & listener class for use with the client connection.
* This is primarily intended to receive messages, but it will also monitor
* the connection to the broker. If the connection is lost, it will attempt
* to restore the connection and re-subscribe to the topic.
*/
class callback : public virtual mqtt::callback, public virtual mqtt::iaction_listener
{
// Counter for the number of connection retries
int nretry_;
// The MQTT client
mqtt::async_client& cli_;
// Options to use if we need to reconnect
mqtt::connect_options& connOpts_;
// An action listener to display the result of actions.
action_listener subListener_;
// This deomonstrates manually reconnecting to the broker by calling
// connect() again. This is a possibility for an application that keeps
// a copy of it's original connect_options, or if the app wants to
// reconnect with different options.
// Another way this can be done manually, if using the same options, is
// to just call the async_client::reconnect() method.
void reconnect()
{
std::this_thread::sleep_for(std::chrono::milliseconds(2500));
try {
cli_.connect(connOpts_, nullptr, *this);
}
catch (const mqtt::exception& exc) {
std::cerr << "Error: " << exc.what() << std::endl;
exit(1);
}
}
// Re-connection failure
void on_failure(const mqtt::token& tok) override
{
std::cout << "Connection attempt failed" << std::endl;
if (++nretry_ > N_RETRY_ATTEMPTS)
exit(1);
reconnect();
}
// (Re)connection success
// Either this or connected() can be used for callbacks.
void on_success(const mqtt::token& tok) override {}
// (Re)connection success
void connected(const std::string& cause) override
{
std::cout << "\nConnection success" << std::endl;
std::cout << "\nSubscribing to topic '" << TOPIC << "'\n"
<< "\tfor client " << CLIENT_ID << " using QoS" << QOS << "\n"
<< "\nPress Q<Enter> to quit\n"
<< std::endl;
cli_.subscribe(TOPIC, QOS, nullptr, subListener_);
}
// Callback for when the connection is lost.
// This will initiate the attempt to manually reconnect.
void connection_lost(const std::string& cause) override
{
std::cout << "\nConnection lost" << std::endl;
if (!cause.empty())
std::cout << "\tcause: " << cause << std::endl;
std::cout << "Reconnecting..." << std::endl;
nretry_ = 0;
reconnect();
}
// Callback for when a message arrives.
void message_arrived(mqtt::const_message_ptr msg) override
{
std::cout << "Message arrived" << std::endl;
std::cout << "\ttopic: '" << msg->get_topic() << "'" << std::endl;
std::cout << "\tpayload: '" << msg->to_string() << "'\n" << std::endl;
}
void delivery_complete(mqtt::delivery_token_ptr token) override {}
public:
callback(mqtt::async_client& cli, mqtt::connect_options& connOpts)
: nretry_(0), cli_(cli), connOpts_(connOpts), subListener_("Subscription")
{
}
};
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
// A subscriber often wants the server to remember its messages when its
// disconnected. In that case, it needs a unique ClientID and a
// non-clean session.
auto serverURI = (argc > 1) ? std::string{argv[1]} : DFLT_SERVER_URI;
mqtt::async_client cli(serverURI, CLIENT_ID);
mqtt::connect_options connOpts;
connOpts.set_clean_session(false);
// Install the callback(s) before connecting.
callback cb(cli, connOpts);
cli.set_callback(cb);
// Start the connection.
// When completed, the callback will subscribe to topic.
try {
std::cout << "Connecting to the MQTT server '" << serverURI << "'..." << std::flush;
cli.connect(connOpts, nullptr, cb);
}
catch (const mqtt::exception& exc) {
std::cerr << "\nERROR: Unable to connect to MQTT server: '" << serverURI << "'" << exc
<< std::endl;
return 1;
}
// Just block till user tells us to quit.
while (std::tolower(std::cin.get()) != 'q');
// Disconnect
try {
std::cout << "\nDisconnecting from the MQTT server..." << std::flush;
cli.disconnect()->wait();
std::cout << "OK" << std::endl;
}
catch (const mqtt::exception& exc) {
std::cerr << exc << std::endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,234 @@
// async_subscribe.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT subscriber using the C++ asynchronous client
// interface, employing callbacks to receive messages and status updates.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker using MQTT v5.
// - Subscribing to a topic
// - Receiving messages through the callback API
// - Displaying MQTT v5 message properties.
// - Receiving network disconnect updates and attempting manual reconnects.
// - Using a "clean session" and manually re-subscribing to topics on
// reconnect.
//
/*******************************************************************************
* Copyright (c) 2013-2025 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
const std::string DFLT_SERVER_URI("mqtt://localhost:1883");
const std::string CLIENT_ID("paho_cpp_async_subscribe");
const std::string TOPIC("#");
const int QOS = 1;
const int N_RETRY_ATTEMPTS = 5;
/////////////////////////////////////////////////////////////////////////////
// Callbacks for the success or failures of requested actions.
// This could be used to initiate further action, but here we just log the
// results to the console.
class action_listener : public virtual mqtt::iaction_listener
{
std::string name_;
void on_failure(const mqtt::token& tok) override
{
std::cout << name_ << " failure";
if (tok.get_message_id() != 0)
std::cout << " for token: [" << tok.get_message_id() << "]" << std::endl;
std::cout << std::endl;
}
void on_success(const mqtt::token& tok) override
{
std::cout << name_ << " success";
if (tok.get_message_id() != 0)
std::cout << " for token: [" << tok.get_message_id() << "]" << std::endl;
auto top = tok.get_topics();
if (top && !top->empty())
std::cout << "\ttoken topic: '" << (*top)[0] << "', ..." << std::endl;
std::cout << std::endl;
}
public:
action_listener(const std::string& name) : name_(name) {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* Local callback & listener class for use with the client connection.
* This is primarily intended to receive messages, but it will also monitor
* the connection to the broker. If the connection is lost, it will attempt
* to restore the connection and re-subscribe to the topic.
*/
class callback : public virtual mqtt::callback, public virtual mqtt::iaction_listener
{
// Counter for the number of connection retries
int nretry_;
// The MQTT client
mqtt::async_client& cli_;
// Options to use if we need to reconnect
mqtt::connect_options& connOpts_;
// An action listener to display the result of actions.
action_listener subListener_;
// This deomonstrates manually reconnecting to the broker by calling
// connect() again. This is a possibility for an application that keeps
// a copy of it's original connect_options, or if the app wants to
// reconnect with different options.
// Another way this can be done manually, if using the same options, is
// to just call the async_client::reconnect() method.
void reconnect()
{
std::this_thread::sleep_for(std::chrono::milliseconds(2500));
try {
cli_.connect(connOpts_, nullptr, *this);
}
catch (const mqtt::exception& exc) {
std::cerr << "Error: " << exc.what() << std::endl;
exit(1);
}
}
// Re-connection failure
void on_failure(const mqtt::token& tok) override
{
std::cout << "Connection attempt failed" << std::endl;
if (++nretry_ > N_RETRY_ATTEMPTS)
exit(1);
reconnect();
}
// (Re)connection success
// Either this or connected() can be used for callbacks.
void on_success(const mqtt::token& tok) override {}
// (Re)connection success
void connected(const std::string& cause) override
{
std::cout << "\nConnection success" << std::endl;
std::cout << "\nSubscribing to topic '" << TOPIC << "'\n"
<< "\tfor client " << CLIENT_ID << " using QoS" << QOS << "\n"
<< "\nPress Q<Enter> to quit\n"
<< std::endl;
cli_.subscribe(TOPIC, QOS, nullptr, subListener_);
}
// Callback for when the connection is lost.
// This will initiate the attempt to manually reconnect.
void connection_lost(const std::string& cause) override
{
std::cout << "\nConnection lost" << std::endl;
if (!cause.empty())
std::cout << "\tcause: " << cause << std::endl;
std::cout << "Reconnecting..." << std::endl;
nretry_ = 0;
reconnect();
}
// Callback for when a message arrives.
void message_arrived(mqtt::const_message_ptr msg) override
{
std::cout << "\nMessage arrived" << std::endl;
std::cout << "\ttopic: '" << msg->get_topic() << "'" << std::endl;
std::cout << "\tpayload: '" << msg->to_string() << std::endl;
const mqtt::properties& props = msg->get_properties();
if (size_t n = props.size(); n != 0) {
std::cout << "\tproperties (" << n << "):\n\t [";
for (size_t i = 0; i < n - 1; ++i) std::cout << props[i] << ", ";
std::cout << props[n - 1] << "]" << std::endl;
}
}
void delivery_complete(mqtt::delivery_token_ptr token) override {}
public:
callback(mqtt::async_client& cli, mqtt::connect_options& connOpts)
: nretry_(0), cli_(cli), connOpts_(connOpts), subListener_("Subscription")
{
}
};
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
// A subscriber often wants the server to remember its messages when its
// disconnected. In that case, it needs a unique ClientID and a
// non-clean session.
auto serverURI = (argc > 1) ? std::string{argv[1]} : DFLT_SERVER_URI;
mqtt::async_client cli(serverURI, CLIENT_ID);
auto connOpts = mqtt::connect_options_builder::v5()
.clean_start(true)
.finalize();
// Install the callback(s) before connecting.
callback cb(cli, connOpts);
cli.set_callback(cb);
// Start the connection.
// When completed, the callback will subscribe to topic.
try {
std::cout << "Connecting to the MQTT server '" << serverURI << "'..." << std::flush;
cli.connect(connOpts, nullptr, cb);
}
catch (const mqtt::exception& exc) {
std::cerr << "\nERROR: Unable to connect to MQTT server: '" << serverURI << "'" << exc
<< std::endl;
return 1;
}
// Just block till user tells us to quit.
while (std::tolower(std::cin.get()) != 'q');
// Disconnect
try {
std::cout << "\nDisconnecting from the MQTT server..." << std::flush;
cli.disconnect()->wait();
std::cout << "OK" << std::endl;
}
catch (const mqtt::exception& exc) {
std::cerr << exc << std::endl;
return 1;
}
return 0;
}

352
examples/data_publish.cpp Normal file
View File

@ -0,0 +1,352 @@
// data_publish.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// It's an example of how to collect and publish periodic data to MQTT, as
// an MQTT publisher using the C++ asynchronous client interface.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker
// - Publishing messages
// - Using a topic object to repeatedly publish to the same topic.
// - Automatic reconnects
// - Off-line buffering
// - User file-based persistence with simple encoding.
//
// This just uses the steady clock to run a periodic loop. Each time
// through, it generates a random number [0-100] as simulated data and
// creates a text, CSV payload in the form:
// <sample #>,<time stamp>,<data>
//
// Note that it uses the steady clock to pace the periodic timing, but then
// reads the system_clock to generate the timestamp for local calendar time.
//
// The sample number is just a counting integer to help test the off-line
// buffering to easily confirm that all the messages got across.
//
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <algorithm>
#include <chrono>
#include <condition_variable>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <mutex>
#include <random>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
using namespace std;
using namespace std::chrono;
namespace fs = std::filesystem;
const std::string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const std::string CLIENT_ID{"paho-cpp-data-publish"};
const string TOPIC{"data/rand"};
const int QOS = 1;
// How often we output a data point
const auto PERIOD = seconds(5);
// The number of out-bound messages we will buffer locally when disconnected.
const int MAX_BUFFERED_MSGS = 120; // 120 * 5sec => 10min off-line buffering
// Top-level directory to keep persistence data
const fs::path PERSIST_DIR{"persist"};
// A key for encoding the persistence data
const string PERSIST_KEY{"elephant"};
// Class to pace timing and signal and exit without delay.
class quit_signal
{
condition_variable cv_;
mutex mtx_;
bool quit_{false};
public:
template <class Clock, class Duration>
bool wait_until(const time_point<Clock, Duration>& abs_time)
{
unique_lock lk(mtx_);
return cv_.wait_until(lk, abs_time, [this] { return quit_; });
}
void signal()
{
unique_lock<mutex> lk(mtx_);
quit_ = true;
lk.unlock();
cv_.notify_one();
}
};
// Variable to pace timing and signal exit
quit_signal quit;
/////////////////////////////////////////////////////////////////////////////
// Example of user-based file persistence with a simple XOR encoding scheme.
//
// Similar to the built-in file persistence, this just creates a
// subdirectory for the persistence data, then places each key into a
// separate file using the key as the file name.
//
// With user-defined persistence, you can transform the data in any way you
// like, such as with encryption/decryption, and you can store the data any
// place you want, such as here with disk files, or use a local DB like
// SQLite or a local key/value store like Redis.
class encoded_file_persistence : virtual public mqtt::iclient_persistence
{
// The directory for the persistence store.
fs::path dir_;
// A key for encoding the data, as supplied by the user
string encodeKey_;
// Simple, in-place XOR encoding and decoding
void encode(string& s) const
{
size_t n = encodeKey_.size();
if (n == 0 || s.empty())
return;
for (size_t i = 0; i < s.size(); ++i) s[i] ^= encodeKey_[i % n];
}
// Gets the persistence file name for the supplied key.
fs::path path_name(const string& key) const { return dir_ / key; }
public:
// Create the persistence object with the specified encoding key
encoded_file_persistence(const string& encodeKey) : encodeKey_(encodeKey) {}
// "Open" the persistence store.
// Create a directory for persistence files, using the client ID and
// serverURI to make a unique directory name. Note that neither can be
// empty. In particular, the app can't use an empty `clientID` if it
// wants to use persistence. (This isn't an absolute rule for your own
// persistence, but you do need a way to keep data from different apps
// separate).
void open(const string& clientId, const string& serverURI) override
{
if (clientId.empty() || serverURI.empty())
throw mqtt::persistence_exception();
// Create a name for the persistence subdirectory for this client
string name = serverURI + "-" + clientId;
std::replace(name.begin(), name.end(), ':', '-');
dir_ = PERSIST_DIR;
dir_ /= name;
fs::create_directories(dir_);
}
// Close the persistent store that was previously opened.
// Remove the persistence directory, if it's empty.
void close() override
{
fs::remove(dir_);
fs::remove(dir_.parent_path());
}
// Clears persistence, so that it no longer contains any persisted data.
// Just remove all the files from the persistence directory.
void clear() override
{
// We could iterate through and remove each file,
// but this does the same thing in fewer steps.
if (!fs::is_empty(dir_)) {
fs::remove_all(dir_);
fs::create_directories(dir_);
}
}
// Returns whether or not data is persisted using the specified key.
// We just look for a file in the store directory with the same name as
// the key.
bool contains_key(const string& key) override
{
if (fs::exists(dir_)) {
for (const auto& entry : fs::directory_iterator(dir_)) {
if (entry.path().filename() == key)
return true;
}
}
return false;
}
// Returns the keys in this persistent data store.
// We just make a collection of the file names in the store directory.
mqtt::string_collection keys() const override
{
mqtt::string_collection ks;
if (fs::exists(dir_)) {
for (const auto& entry : fs::directory_iterator(dir_)) {
ks.push_back(entry.path().filename().string());
}
}
return ks;
}
// Puts the specified data into the persistent store.
// We just encode the data and write it to a file using the key as the
// name of the file. The multiple buffers given here need to be written
// in order - and a scatter/gather like writev() would be fine. But...
// the data will be read back as a single buffer, so here we first
// concat a string so that the encoding key lines up with the data the
// same way it will on the read-back.
void put(const string& key, const std::vector<mqtt::string_view>& bufs) override
{
auto path = path_name(key);
ofstream os(path, ios_base::binary);
if (!os)
throw mqtt::persistence_exception();
string s;
for (const auto& b : bufs) s.append(b.data(), b.size());
encode(s);
os.write(s.data(), s.size());
}
// Gets the specified data out of the persistent store.
// We look for a file with the name of the key, read the contents,
// decode, and return it.
string get(const string& key) const override
{
auto path = path_name(key);
ifstream is(path, ios_base::ate | ios_base::binary);
if (!is)
throw mqtt::persistence_exception();
// Read the whole file into a string
streamsize sz = is.tellg();
if (sz == 0)
return string();
is.seekg(0);
string s(sz, '\0');
is.read(&s[0], sz);
if (is.gcount() < sz)
s.resize(is.gcount());
encode(s);
return s;
}
// Remove the data for the specified key.
// Just remove the file with the same name as the key, if found.
void remove(const string& key) override
{
auto path = path_name(key);
fs::remove(path);
}
};
/////////////////////////////////////////////////////////////////////////////
// Handler for ^C (SIGINT)
void ctrlc_handler(int) { quit.signal(); }
// --------------------------------------------------------------------------
int main(int argc, char* argv[])
{
string serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI;
// Create a persistence object
encoded_file_persistence persist{PERSIST_KEY};
// Create a client to use the persistence.
mqtt::async_client cli(serverURI, CLIENT_ID, MAX_BUFFERED_MSGS, &persist);
auto connOpts = mqtt::connect_options_builder()
.keep_alive_interval(MAX_BUFFERED_MSGS * PERIOD)
.clean_session(false)
.automatic_reconnect(true)
.finalize();
// Create a topic object. This is a conventience since we will
// repeatedly publish messages with the same parameters.
mqtt::topic top(cli, TOPIC, QOS, true);
// Random number generator [0 - 100]
random_device rnd;
mt19937 gen(rnd());
uniform_int_distribution<> dis(0, 100);
try {
// Connect to the MQTT broker
cout << "Connecting to server '" << serverURI << "'..." << flush;
cli.connect(connOpts)->wait();
cout << "OK\n" << endl;
char tmbuf[32];
unsigned nsample = 0;
// Install a ^C handler for user to signal when to exit
signal(SIGINT, ctrlc_handler);
// The steady time at which to read the next sample
auto tm = steady_clock::now() + 250ms;
// Pace the sampling by letting the condition variable time out
// periodically. When 'quit' is signaled, it's time to quit.
while (!quit.wait_until(tm)) {
// Get a timestamp and format as a string
time_t t = system_clock::to_time_t(system_clock::now());
strftime(tmbuf, sizeof(tmbuf), "%F %T", localtime(&t));
// Simulate reading some data
int x = dis(gen);
// Create the payload as a text CSV string
string payload = to_string(++nsample) + "," + tmbuf + "," + to_string(x);
cout << payload << endl;
// Publish to the topic
top.publish(std::move(payload));
tm += PERIOD;
}
// Disconnect
cout << "\nDisconnecting..." << flush;
cli.disconnect()->wait();
cout << "OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.what() << endl;
return 1;
}
return 0;
}

157
examples/mqttpp_chat.cpp Normal file
View File

@ -0,0 +1,157 @@
// mqttpp_chat.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// The "chat" application is practically the "Hello World" application for
// messaging systems. This allows a user to type in message to send to a
// "group" while seeing all the messages that the other members of the group
// send.
//
// This application is an MQTT publisher/subscriber using the C++
// asynchronous client interface, employing callbacks to receive messages
// and status updates.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker.
// - Publishing messages.
// - Subscribing to a topic
// - Receiving messages (callbacks) through a lambda function
//
// USAGE:
// mqttpp_chat <user> <group>
/*******************************************************************************
* Copyright (c) 2019-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
#include "mqtt/topic.h"
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
// The broker/server address
const std::string SERVER_ADDRESS("mqtt://localhost:1883");
// The QoS to use for publishing and subscribing
const int QOS = 1;
// Tell the broker we don't want our own messages sent back to us.
const bool NO_LOCAL = true;
if (argc != 3) {
std::cout << "USAGE: mqttpp_chat <user> <group>" << std::endl;
return 1;
}
std::string chatUser{argv[1]}, chatGroup{argv[2]}, chatTopic{"chat/" + chatGroup};
mqtt::async_client cli(SERVER_ADDRESS);
// LWT message is broadcast to other users if out connection is lost
auto lwt =
mqtt::message(chatTopic, "<<<" + chatUser + " was disconnected>>>", QOS, false);
// Set up the connect options
auto connOpts = mqtt::connect_options_builder()
.properties({{mqtt::property::SESSION_EXPIRY_INTERVAL, 604800}})
.clean_start(false)
.will(std::move(lwt))
.keep_alive_interval(std::chrono::seconds(20))
.finalize();
// Set a callback for connection lost.
// This just exits the app.
cli.set_connection_lost_handler([](const std::string&) {
std::cout << "*** Connection Lost ***" << std::endl;
exit(2);
});
// Set the callback for incoming messages
cli.set_message_callback([](mqtt::const_message_ptr msg) {
std::cout << msg->get_payload_str() << std::endl;
});
// We publish and subscribe to one topic,
// so a 'topic' object is helpful.
mqtt::topic topic{cli, "chat/" + chatGroup, QOS};
// Start the connection.
try {
std::cout << "Connecting to the chat server at '" << SERVER_ADDRESS << "'..."
<< std::flush;
auto tok = cli.connect(connOpts);
tok->wait();
// Subscribe to the topic using "no local" so that
// we don't get own messages sent back to us
std::cout << "Ok\nJoining the group..." << std::flush;
auto subOpts = mqtt::subscribe_options(NO_LOCAL);
topic.subscribe(subOpts)->wait();
std::cout << "Ok" << std::endl;
}
catch (const mqtt::exception& exc) {
std::cerr << "\nERROR: Unable to connect. " << exc.what() << std::endl;
return 1;
}
// Let everyone know that a new user joined the conversation.
topic.publish("<<" + chatUser + " joined the group>>");
// Read messages from the console and publish them.
// Quit when the use enters an empty line.
std::string usrMsg;
while (std::getline(std::cin, usrMsg) && !usrMsg.empty()) {
usrMsg = chatUser + ": " + usrMsg;
topic.publish(usrMsg);
}
// Let everyone know that the user left the conversation.
topic.publish("<<" + chatUser + " left the group>>")->wait();
// Disconnect
try {
std::cout << "Disconnecting from the chat server..." << std::flush;
cli.disconnect()->wait();
std::cout << "OK" << std::endl;
}
catch (const mqtt::exception& exc) {
std::cerr << exc.what() << std::endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,242 @@
// async_subscribe.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT publisher/subscriber using the C++
// asynchronous client interface, demonstrating how you can share a client
// between multiple threads.
//
// The app will count the number of "data" messages arriving at the broker
// and then emit "events" with updated counts. A data message is any on a
// "data/#" topic, and counts are emitted on the "events/count" topic. It
// emits an event count around once every ten data messages.
//
// Note that this is a fairly contrived example, and it could be done much
// more easily in a single thread. It is meant to demonstrate how you can
// share a client amongst threads if and when that's a proper thing to do.
//
// At this time, there is a single callback or consumer queue for all
// incoming messages, so you would typically only have one thead receiving
// messages, although it _could_ send messages to multiple threads for
// processing, perhaps based on the topics. It could be common, however, to
// want to have multiple threads for publishing.
//
// This example demonstrates:
// - Creating a client and sharing it across threads using a shared_ptr<>
// - Using one thread to receive incoming messages from the broker and
// another thread to publish messages to it.
// - Connecting to an MQTT server/broker.
// - Automatic reconnect
// - Publishing messages
// - Subscribing to multiple topics
// - Using the asynchronous message consumer
// - Signaling consumer from another thread
//
/*******************************************************************************
* Copyright (c) 2020-2025 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
using namespace std;
using namespace std::chrono;
const std::string DFLT_SERVER_ADDRESS{"mqtt://localhost:1883"};
const std::string CLIENT_ID{"multithr_pub_sub_cpp"};
/////////////////////////////////////////////////////////////////////////////
/**
* A thread-safe counter that can be used to occasionally signal a waiter on
* every 10th increment.
*/
class multithr_counter
{
using guard = std::unique_lock<std::mutex>;
size_t count_;
bool closed_;
mutable bool ready_;
mutable std::condition_variable cond_;
mutable std::mutex lock_;
public:
// Declare a pointer type for sharing a counter between threads
using ptr_t = std::shared_ptr<multithr_counter>;
// Create a new thread-safe counter with an initial count of zero.
multithr_counter() : count_(0), closed_(false), ready_(false) {}
// Determines if the counter has been closed.
bool closed() const
{
guard g(lock_);
return closed_;
}
// Close the counter and signal all waiters.
void close()
{
guard g(lock_);
closed_ = ready_ = true;
cond_.notify_all();
}
// Increments the count, and then signals once every 10 messages.
void incr()
{
guard g(lock_);
if (closed_)
throw string("Counter is closed");
if (++count_ % 10 == 0) {
ready_ = true;
g.unlock();
cond_.notify_all();
}
}
// This will block the caller until at least 10 new messages received.
size_t get_count() const
{
guard g(lock_);
cond_.wait(g, [this] { return ready_; });
ready_ = false;
return count_;
}
};
/////////////////////////////////////////////////////////////////////////////
// The MQTT publisher function will run in its own thread.
// It runs until the receiver thread closes the counter object.
void publisher_func(mqtt::async_client_ptr cli, multithr_counter::ptr_t counter)
{
while (true) {
size_t n = counter->get_count();
if (counter->closed())
break;
string payload = std::to_string(n);
cli->publish("events/count", payload)->wait();
}
}
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
string address = (argc > 1) ? string(argv[1]) : DFLT_SERVER_ADDRESS;
// Create an MQTT client using a smart pointer to be shared among threads.
auto cli = std::make_shared<mqtt::async_client>(address, CLIENT_ID);
// Make a counter object also with a shared pointer.
auto counter = std::make_shared<multithr_counter>();
// Connect options for a persistent session and automatic reconnects.
auto connOpts = mqtt::connect_options_builder()
.clean_session(false)
.automatic_reconnect(seconds(2), seconds(30))
.finalize();
auto TOPICS = mqtt::string_collection::create({"data/#", "command"});
const vector<int> QOS{0, 1};
try {
// Start consuming _before_ connecting, because we could get a flood
// of stored messages as soon as the connection completes since
// we're using a persistent (non-clean) session with the broker.
cli->start_consuming();
cout << "Connecting to the MQTT server at " << address << "..." << flush;
auto rsp = cli->connect(connOpts)->get_connect_response();
cout << "OK\n" << endl;
cout << "Now start an application such as 'async_publish_time'\n"
<< "that publishes to a 'data/' topic...\n"
<< endl;
// Subscribe if this is a new session with the server
if (!rsp.is_session_present())
cli->subscribe(TOPICS, QOS);
// Start the publisher thread
std::thread publisher(publisher_func, cli, counter);
// Start another thread to shut us down after a minute
std::thread{[cli] {
this_thread::sleep_for(30s);
cout << "Signaling the consumer to stop." << endl;
cli->stop_consuming();
}}.detach();
// Consume messages in this thread
// Remember that with the message consumer, we can't detect a
// reconnect We would need to register a connect callback or use the
// event consumer.
while (true) {
auto msg = cli->consume_message();
if (!msg) {
// Exit if the consumer was shut down
if (cli->consumer_closed())
break;
// Otherwise let auto-reconnect deal with it.
cout << "Disconnect detected. Attempting an auto-reconnect." << endl;
continue;
}
if (msg->get_topic() == "command" && msg->to_string() == "exit") {
cout << "Exit command received" << endl;
break;
}
cout << msg->get_topic() << ": " << msg->to_string() << endl;
counter->incr();
}
// Close the counter and wait for the publisher thread to complete
cout << "\nShutting down..." << flush;
counter->close();
publisher.join();
// Disconnect
cout << "OK\nDisconnecting..." << flush;
cli->disconnect();
cout << "OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.what() << endl;
return 1;
}
return 0;
}

150
examples/pub_speed_test.cpp Normal file
View File

@ -0,0 +1,150 @@
// pub_speed_test.cpp
//
// Paho C++ sample client application to do a simple test of the speed at
// which messages can be published.
//
/*******************************************************************************
* Copyright (c) 2013-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <future>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
#include "mqtt/thread_queue.h"
using namespace std;
using namespace std::chrono;
const std::string DFLT_SERVER_ADDRESS{"mqtt://localhost:1883"};
const size_t DFLT_PAYLOAD_SIZE = 1024;
const int DFLT_N_MSG = 1000, DFLT_QOS = 1;
const string TOPIC{"test/speed"};
const char* LWT_PAYLOAD = "pub_speed_test died unexpectedly.";
// Queue for passing tokens to the wait thread
mqtt::thread_queue<mqtt::delivery_token_ptr> que;
// Get the current time on the steady clock
steady_clock::time_point now() { return steady_clock::now(); }
// Convert a duration to a count of milliseconds
template <class Rep, class Period>
int64_t msec(const std::chrono::duration<Rep, Period>& dur)
{
return (int64_t)duration_cast<milliseconds>(dur).count();
}
// --------------------------------------------------------------------------
// Thread function will wait for all the tokens to complete.
// Any exceptions thrown from here will be caught in main().
void token_wait_func()
{
while (true) {
mqtt::delivery_token_ptr tok = que.get();
if (!tok)
break;
// cout.put('x');
tok->wait();
}
}
// --------------------------------------------------------------------------
int main(int argc, char* argv[])
{
string address = (argc > 1) ? string(argv[1]) : DFLT_SERVER_ADDRESS;
int nMsg = (argc > 2) ? atoi(argv[2]) : DFLT_N_MSG;
size_t msgSz = (size_t)((argc > 3) ? atol(argv[3]) : DFLT_PAYLOAD_SIZE);
int qos = (argc > 4) ? atoi(argv[4]) : DFLT_QOS;
cout << "Initializing for server '" << address << "'..." << flush;
mqtt::async_client cli(address, "");
mqtt::message willmsg(TOPIC, LWT_PAYLOAD, 1, true);
mqtt::will_options will(willmsg);
mqtt::connect_options connOpts;
connOpts.set_clean_session(true);
connOpts.set_will(will);
// Create a payload
mqtt::binary payload;
for (size_t i = 0; i < msgSz; ++i) payload.push_back('a' + i % 26);
cout << "OK" << endl;
try {
// Create the message (move payload into it)
auto msg = mqtt::make_message(TOPIC, std::move(payload), qos, false);
// Connect to the broker
cout << "\nConnecting..." << flush;
auto start = now();
cli.connect(connOpts)->wait();
auto end = now();
cout << "OK" << endl;
cout << "Connected in " << msec(end - start) << "ms" << endl;
auto fut = std::async(launch::async, token_wait_func);
// Publish the messages
cout << "\nPublishing " << nMsg << " messages..." << flush;
start = now();
for (int i = 0; i < nMsg; ++i) {
auto dtok = cli.publish(msg);
// cout.put('^');
que.put(std::move(dtok));
}
auto pubend = now();
que.put(mqtt::delivery_token_ptr());
// Wait for all the tokens to complete
fut.get();
end = now();
cout << "OK" << endl;
auto ms = msec(pubend - start);
cout << "Published in " << ms << "ms " << (nMsg / ms) << "k msg/sec" << endl;
ms = msec(end - start);
cout << "Acknowledged in " << ms << "ms " << (nMsg / ms) << "k msg/sec" << endl;
// Disconnect
cout << "\nDisconnecting..." << flush;
start = now();
cli.disconnect(seconds(10))->wait();
end = now();
cout << "OK" << endl;
cout << "Disconnected in " << msec(end - start) << "ms" << endl;
}
catch (const mqtt::exception& exc) {
que.put(mqtt::delivery_token_ptr{});
cerr << exc.what() << endl;
return 1;
}
return 0;
}

152
examples/rpc_math_cli.cpp Normal file
View File

@ -0,0 +1,152 @@
// rpc_math_cli.cpp
//
// This is a Paho MQTT v5 C++ sample application.
//
// It's an example of how to create a client for performing remote procedure
// calls using MQTT with the 'response topic' and 'correlation data'
// properties.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker
// - Using MQTT v5 properties
// - Publishing RPC request messages
// - Using asynchronous tokens
// - Subscribing to reply topic
//
/*******************************************************************************
* Copyright (c) 2019-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
#include "mqtt/properties.h"
using namespace std;
using namespace std::chrono;
const string SERVER_ADDRESS{"mqtt://localhost:1883"};
const auto TIMEOUT = std::chrono::seconds(10);
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
if (argc < 4) {
cout << "USAGE: rpc_math_cli <add|mult> <num1> <num2> [... numN]" << endl;
return 1;
}
constexpr int QOS = 1;
const string REQ_TOPIC_HDR{"requests/math/"};
// Create a client
mqtt::async_client cli(SERVER_ADDRESS, "");
cli.start_consuming();
try {
cout << "Connecting..." << flush;
auto connOpts = mqtt::connect_options::v5();
mqtt::token_ptr tok = cli.connect(connOpts);
auto connRsp = tok->get_connect_response();
cout << "OK (" << connRsp.get_server_uri() << ")" << endl;
// Since we gave an empty client ID, the server should create a
// unique one for us and send it back as ASSIGNED_CLIENT_IDENTIFIER
// in the connect properties.
string clientId =
get<string>(connRsp.get_properties(), mqtt::property::ASSIGNED_CLIENT_IDENTIFIER);
// So now we can create a unique RPC response topic using
// the assigned (unique) client ID.
string repTopic = "replies/" + clientId + "/math";
cout << " Reply topic: " << repTopic << endl;
// Subscribe to the reply topic and verify the QoS
tok = cli.subscribe(repTopic, QOS);
tok->wait();
if (int(tok->get_reason_code()) != QOS) {
cerr << "Error: Server doesn't support reply QoS: [" << tok->get_reason_code()
<< "]" << endl;
return 2;
}
// Create and send the request message
string req{argv[1]}, reqTopic{REQ_TOPIC_HDR + req};
mqtt::properties props{
{mqtt::property::RESPONSE_TOPIC, repTopic},
{mqtt::property::CORRELATION_DATA, "1"}
};
ostringstream os;
os << "[ ";
for (int i = 2; i < argc - 1; ++i) os << argv[i] << ", ";
os << argv[argc - 1] << " ]";
string reqArgs{os.str()};
cout << "\nSending '" << req << "' request " << os.str() << "..." << flush;
auto pubmsg = mqtt::message_ptr_builder()
.topic(reqTopic)
.payload(reqArgs)
.qos(QOS)
.properties(props)
.finalize();
cli.publish(pubmsg)->wait_for(TIMEOUT);
cout << "OK" << endl;
// Wait for reply.
auto msg = cli.try_consume_message_for(seconds(5));
if (!msg) {
cerr << "Didn't receive a reply from the service." << endl;
return 1;
}
cout << " Result: " << msg->to_string() << endl;
// Unsubscribe
cli.unsubscribe(repTopic)->wait();
// Disconnect
cout << "\nDisconnecting..." << flush;
cli.disconnect()->wait();
cout << "OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.what() << endl;
return 1;
}
return 0;
}

194
examples/rpc_math_srvr.cpp Normal file
View File

@ -0,0 +1,194 @@
// rpc_math_srvr.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT consumer/subscriber using the C++ synchronous
// client interface, which uses the queuing API to receive messages.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker
// - Subscribing to multiple topics
// - Receiving messages through the queueing consumer API
// - Receiving and acting upon commands via MQTT topics
// - Manual reconnects
// - Using a persistent (non-clean) session
//
/*******************************************************************************
* Copyright (c) 2019-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#include "mqtt/client.h"
using namespace std;
using namespace std::chrono;
const string SERVER_ADDRESS{"mqtt://localhost:1883"};
const string CLIENT_ID{"rpc_math_srvr"};
constexpr auto RESPONSE_TOPIC = mqtt::property::RESPONSE_TOPIC;
constexpr auto CORRELATION_DATA = mqtt::property::CORRELATION_DATA;
// --------------------------------------------------------------------------
// Simple function to manually reconnect a client.
bool try_reconnect(mqtt::client& cli)
{
constexpr int N_ATTEMPT = 30;
for (int i = 0; i < N_ATTEMPT && !cli.is_connected(); ++i) {
try {
cli.reconnect();
return true;
}
catch (const mqtt::exception&) {
this_thread::sleep_for(seconds(1));
}
}
return false;
}
// --------------------------------------------------------------------------
// RPC function implementations
double add(const std::vector<double>& nums)
{
double sum = 0.0;
for (auto n : nums) sum += n;
return sum;
}
double mult(const std::vector<double>& nums)
{
double prod = 1.0;
for (auto n : nums) prod *= n;
return prod;
}
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
mqtt::create_options createOpts(MQTTVERSION_5);
mqtt::client cli(SERVER_ADDRESS, CLIENT_ID, createOpts);
auto connOpts = mqtt::connect_options_builder()
.keep_alive_interval(seconds(20))
.clean_start()
.finalize();
const vector<string> TOPICS{"requests/math", "requests/math/#"};
const vector<int> QOS{1, 1};
try {
cout << "Connecting to the MQTT server..." << flush;
cli.connect(connOpts);
cli.subscribe(TOPICS, QOS);
cout << "OK\n" << endl;
// Consume messages
cout << "Waiting for RPC requests..." << endl;
while (true) {
auto msg = cli.consume_message();
if (!msg) {
if (!cli.is_connected()) {
cout << "Lost connection. Attempting reconnect" << endl;
if (try_reconnect(cli)) {
cli.subscribe(TOPICS, QOS);
cout << "Reconnected" << endl;
continue;
}
else {
cout << "Reconnect failed." << endl;
break;
}
}
else
break;
}
cout << "Received a request" << endl;
const mqtt::properties& props = msg->get_properties();
if (props.contains(RESPONSE_TOPIC) && props.contains(CORRELATION_DATA)) {
mqtt::binary corr_id = mqtt::get<string>(props, CORRELATION_DATA);
string reply_to = mqtt::get<string>(props, RESPONSE_TOPIC);
cout << "Client wants a reply to [" << corr_id << "] on '" << reply_to << "'"
<< endl;
cout << msg->get_topic() << ": " << msg->to_string() << endl;
char c;
double x;
vector<double> nums;
istringstream is(msg->to_string());
if (!(is >> c) || c != '[') {
cout << "Malformed arguments" << endl;
// Maybe send an error message to client.
continue;
}
c = ',';
while (c == ',' && (is >> x >> c)) nums.push_back(x);
if (c != ']') {
cout << "Bad closing delimiter" << endl;
continue;
}
x = 0.0;
if (msg->get_topic() == "requests/math/add")
x = add(nums);
else if (msg->get_topic() == "requests/math/mult")
x = mult(nums);
else {
cout << "Unknown request: " << msg->get_topic() << endl;
continue;
}
cout << " Result: " << x << endl;
auto reply_msg = mqtt::message::create(reply_to, to_string(x), 1, false);
cli.publish(reply_msg);
}
}
// Disconnect
cout << "\nDisconnecting from the MQTT server..." << flush;
cli.disconnect();
cout << "OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.what() << endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,117 @@
// server_props_v5.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT client using the C++ asynchronous interface
// which shows how to check the server cofiguration for an MQTT v5
// connection.
//
// With an MQTT v5 connection, the server specify can some features that it
// doesn't supports, or limits in some way. It does this by adding v5
// properties to the CONNACK packet it sends back to the client in a connect
// transaction. The C++ application can retrieve these from the connect
// token via the `connect_response` object.
//
// It also shows short-lived persistent sessions. The client asks the server
// to just keep the session for 10sec. If you re-run the application in less
// than 10sec, it should report that the session exists. Any longer, and the
// session will be gone.
//
// Note that 10sec is probably *way too short* a time for real-world
// applications. This is just for demonstrating/testing the session expiry
// interval.
//
// The sample demonstrates:
// - Connecting to an MQTT v5 server/broker.
// - Specifying a short-lived (10sec) persistent session.
// - Retrieving the v5 properties from the connect response (i.e. CONNACK
// packet)
// - Iterating through v5 properties.
// - Displaying server properties to the user.
//
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
using namespace std;
const string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const string CLIENT_ID{"server_props_v5"};
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
auto serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI;
mqtt::async_client cli(serverURI, CLIENT_ID);
auto connOpts = mqtt::connect_options_builder::v5()
.clean_start(false)
.properties({{mqtt::property::SESSION_EXPIRY_INTERVAL, 10}})
.finalize();
try {
// Connect to the server
cout << "Connecting to the MQTT server at '" << serverURI << "'..." << flush;
auto tok = cli.connect(connOpts);
// Getting the connect response will block waiting for the
// connection to complete.
auto rsp = tok->get_connect_response();
cout << "OK" << endl;
// Make sure we were granted a v5 connection.
if (rsp.get_mqtt_version() < MQTTVERSION_5) {
cout << "Did not get an MQTT v5 connection." << endl;
exit(1);
}
// Does the server have a session for us?
cout << "\nThe session is " << (rsp.is_session_present() ? "" : "not ")
<< "present on the server." << endl;
// Show the v5 properties from the CONNACK, if any
cout << "\nConnection Properties:" << endl;
if (rsp.get_properties().empty()) {
cout << " <none>" << endl;
}
else {
for (const auto& prop : rsp.get_properties()) {
cout << " " << prop << endl;
}
}
// OK, we're done.
cli.disconnect()->wait();
}
catch (const mqtt::exception& exc) {
cerr << "\n " << exc << endl;
return 1;
}
return 0;
}

170
examples/ssl_publish.cpp Normal file
View File

@ -0,0 +1,170 @@
// ssl_publish.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// It's an example of how to connect to an MQTT broker securely, and then
// send messages as an MQTT publisher using the C++ asynchronous client
// interface.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker securely
// - Setting SSL/TLS options
// - Last will and testament
// - Publishing messages
// - Using asynchronous tokens
// - Implementing callbacks and action listeners
//
// We can test this using mosquitto configured with certificates in the
// Paho C library. The C library has an SSL/TSL test suite, and we can use
// that to test:
// $ cd paho.mqtt.c
// $ mosquitto -c test/tls-testing/mosquitto.conf
//
// Then use the files "test-root-ca.crt" and "client.pem" from the
// test/ssl directory (paho.mqtt.c/test/ssl) for the trust store and
// key_store, respectively, for this program.
//
/*******************************************************************************
* Copyright (c) 2013-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include "mqtt/async_client.h"
const std::string DFLT_SERVER_URI{"mqtts://localhost:18884"};
const std::string DFLT_CLIENT_ID{"ssl_publish_cpp"};
const std::string KEY_STORE{"client.pem"};
const std::string TRUST_STORE{"test-root-ca.crt"};
const std::string LWT_TOPIC{"events/disconnect"};
const std::string LWT_PAYLOAD{"Last will and testament."};
const int QOS = 1;
const auto TIMEOUT = std::chrono::seconds(10);
/////////////////////////////////////////////////////////////////////////////
/**
* A callback class for use with the main MQTT client.
*/
class callback : public virtual mqtt::callback
{
public:
void connection_lost(const std::string& cause) override
{
std::cout << "\nConnection lost" << std::endl;
if (!cause.empty())
std::cout << "\tcause: " << cause << std::endl;
}
void delivery_complete(mqtt::delivery_token_ptr tok) override
{
std::cout << "\tDelivery complete for token: " << (tok ? tok->get_message_id() : -1)
<< std::endl;
}
};
/////////////////////////////////////////////////////////////////////////////
using namespace std;
int main(int argc, char* argv[])
{
string serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI,
clientID = (argc > 2) ? string{argv[2]} : DFLT_CLIENT_ID;
// Note that we don't actually need to open the trust or key stores.
// We just need a quick, portable way to check that they exist.
{
ifstream tstore(TRUST_STORE);
if (!tstore) {
cerr << "The trust store file does not exist: " << TRUST_STORE << endl;
cerr << " Get a copy from \"paho.mqtt.c/test/ssl/test-root-ca.crt\"" << endl;
;
return 1;
}
ifstream kstore(KEY_STORE);
if (!kstore) {
cerr << "The key store file does not exist: " << KEY_STORE << endl;
cerr << " Get a copy from \"paho.mqtt.c/test/ssl/client.pem\"" << endl;
return 1;
}
}
cout << "Initializing for server '" << serverURI << "'..." << endl;
mqtt::async_client client(serverURI, clientID);
callback cb;
client.set_callback(cb);
// Build the connect options, including SSL and a LWT message.
auto sslopts = mqtt::ssl_options_builder()
.trust_store(TRUST_STORE)
.key_store(KEY_STORE)
.error_handler([](const std::string& msg) {
std::cerr << "SSL Error: " << msg << std::endl;
})
.finalize();
auto willmsg = mqtt::message(LWT_TOPIC, LWT_PAYLOAD, QOS, true);
auto connopts = mqtt::connect_options_builder()
.user_name("testuser")
.password("testpassword")
.will(std::move(willmsg))
.ssl(std::move(sslopts))
.finalize();
cout << " ...OK" << endl;
try {
// Connect using SSL/TLS
cout << "\nConnecting..." << endl;
mqtt::token_ptr conntok = client.connect(connopts);
cout << "Waiting for the connection..." << endl;
conntok->wait();
cout << " ...OK" << endl;
// Send a message
cout << "\nSending message..." << endl;
auto msg = mqtt::make_message("hello", "Hello secure C++ world!", QOS, false);
client.publish(msg)->wait_for(TIMEOUT);
cout << " ...OK" << endl;
// Disconnect
cout << "\nDisconnecting..." << endl;
client.disconnect()->wait();
cout << " ...OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.what() << endl;
return 1;
}
return 0;
}

131
examples/sync_consume.cpp Normal file
View File

@ -0,0 +1,131 @@
// sync_consume.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT consumer/subscriber using the C++ synchronous
// client interface, which uses the queuing API to receive messages.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker
// - Using a persistent (non-clean) session
// - Subscribing to multiple topics
// - Receiving messages through the queueing consumer API
// - Receiving and acting upon commands via MQTT topics
// - Auto reconnect
// - Updating auto-reconnect data
//
/*******************************************************************************
* Copyright (c) 2013-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/client.h"
using namespace std;
using namespace std::chrono;
const string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const string CLIENT_ID{"paho_cpp_sync_consume"};
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
auto serverURI = (argc > 1) ? std::string{argv[1]} : DFLT_SERVER_URI;
mqtt::client cli(serverURI, CLIENT_ID);
auto connOpts = mqtt::connect_options_builder::v3()
.user_name("user")
.password("passwd")
.keep_alive_interval(seconds(30))
.automatic_reconnect(seconds(2), seconds(30))
.clean_session(false)
.finalize();
// You can install a callback to change some connection data
// on auto reconnect attempts. To make a change, update the
// `connect_data` and return 'true'.
cli.set_update_connection_handler([](mqtt::connect_data& connData) {
string newUserName{"newuser"};
if (connData.get_user_name() == newUserName)
return false;
cout << "Previous user: '" << connData.get_user_name() << "'" << endl;
connData.set_user_name(newUserName);
cout << "New user name: '" << connData.get_user_name() << "'" << endl;
return true;
});
const vector<string> TOPICS{"data/#", "command"};
const vector<int> QOS{0, 1};
try {
cout << "Connecting to the MQTT server..." << flush;
mqtt::connect_response rsp = cli.connect(connOpts);
cout << "OK\n" << endl;
if (!rsp.is_session_present()) {
std::cout << "Subscribing to topics..." << std::flush;
cli.subscribe(TOPICS, QOS);
std::cout << "OK" << std::endl;
}
else {
cout << "Session already present. Skipping subscribe." << std::endl;
}
// Consume messages
while (true) {
auto msg = cli.consume_message();
if (msg) {
if (msg->get_topic() == "command" && msg->to_string() == "exit") {
cout << "Exit command received" << endl;
break;
}
cout << msg->get_topic() << ": " << msg->to_string() << endl;
}
else if (!cli.is_connected()) {
cout << "Lost connection" << endl;
while (!cli.is_connected()) {
this_thread::sleep_for(milliseconds(250));
}
cout << "Re-established connection" << endl;
}
}
// Disconnect
cout << "\nDisconnecting from the MQTT server..." << flush;
cli.disconnect();
cout << "OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.what() << endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,162 @@
// sync_consume_v5.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT consumer/subscriber using the C++ synchronous
// client interface, which uses the queuing API to receive messages.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker
// - Using a persistent (non-clean) session
// - Subscribing to multiple topics
// - Receiving messages through the queueing consumer API
// - Receiving and acting upon commands via MQTT topics
// - Auto reconnect
// - Updating auto-reconnect data
//
/*******************************************************************************
* Copyright (c) 2020-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include "mqtt/client.h"
using namespace std;
using namespace std::chrono;
const string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const string CLIENT_ID{"paho_cpp_sync_consume5"};
constexpr int QOS_0 = 0;
constexpr int QOS_1 = 1;
// Infinite time for session expiration
const uint32_t INFINITE = std::numeric_limits<uint32_t>::max();
/////////////////////////////////////////////////////////////////////////////
// Message table function signature
using handler_t = std::function<bool(const mqtt::message&)>;
// Handler for data messages (i.e. topic "data/#")
bool data_handler(const mqtt::message& msg)
{
cout << msg.get_topic() << ": " << msg.to_string() << endl;
return true;
}
// Handler for command messages (i.e. topic "command")
// Return false to exit the application
bool command_handler(const mqtt::message& msg)
{
if (msg.to_string() == "exit") {
cout << "Exit command received" << endl;
return false;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
auto serverURI = (argc > 1) ? std::string{argv[1]} : DFLT_SERVER_URI;
mqtt::client cli(serverURI, CLIENT_ID, mqtt::create_options(MQTTVERSION_5));
auto connOpts = mqtt::connect_options_builder::v5()
.automatic_reconnect(seconds(2), seconds(30))
.clean_start(false)
.properties({{mqtt::property::SESSION_EXPIRY_INTERVAL, INFINITE}})
.finalize();
// Dispatch table to handle incoming messages based on Subscription ID's.
std::vector<handler_t> handler{data_handler, command_handler};
try {
cout << "Connecting to the MQTT server..." << flush;
mqtt::connect_response rsp = cli.connect(connOpts);
cout << "OK\n" << endl;
if (!rsp.is_session_present()) {
std::cout << "Subscribing to topics..." << std::flush;
mqtt::subscribe_options subOpts;
mqtt::properties props1{
{mqtt::property::SUBSCRIPTION_IDENTIFIER, 1},
};
cli.subscribe("data/#", QOS_0, subOpts, props1);
mqtt::properties props2{
{mqtt::property::SUBSCRIPTION_IDENTIFIER, 2},
};
cli.subscribe("command", QOS_1, subOpts, props2);
std::cout << "OK" << std::endl;
}
else {
cout << "Session already present. Skipping subscribe." << std::endl;
}
// Consume messages
while (true) {
auto msg = cli.consume_message();
// Note: In a real app, you'd want to do a lot more error
// and bounds checking than this.
if (msg) {
// Get the subscription ID from the incoming message
auto subId = mqtt::get<uint32_t>(
msg->get_properties(), mqtt::property::SUBSCRIPTION_IDENTIFIER
);
// Dispatch to a handler function based on the Subscription ID
if (!(handler[subId - 1])(*msg))
break;
}
else if (!cli.is_connected()) {
cout << "Lost connection" << endl;
while (!cli.is_connected()) {
this_thread::sleep_for(milliseconds(250));
}
cout << "Re-established connection" << endl;
}
}
// Disconnect
cout << "\nDisconnecting from the MQTT server..." << flush;
cli.disconnect();
cout << "OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.what() << endl;
return 1;
}
return 0;
}

224
examples/sync_publish.cpp Normal file
View File

@ -0,0 +1,224 @@
// sync_publish.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// It's an example of how to send messages as an MQTT publisher using the
// C++ synchronous client interface.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker
// - Publishing messages
// - User-defined persistence
//
/*******************************************************************************
* Copyright (c) 2013-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include "mqtt/client.h"
const std::string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const std::string TOPIC{"hello"};
const std::string PAYLOAD1{"Hello World!"};
const char* PAYLOAD2 = "Hi there!";
const char* PAYLOAD3 = "Is anyone listening?";
const int QOS = 1;
/////////////////////////////////////////////////////////////////////////////
// Example of a simple, in-memory persistence class.
//
// This is an extremely silly example, because if you want to use
// persistence, you actually need it to be out of process so that if the
// client crashes and restarts, the persistence data still exists.
//
// This is just here to show how the persistence API callbacks work. It maps
// well to key/value stores, like Redis, but only if it's on the local host,
// as it wouldn't make sense to persist data over the network, since that's
// what the MQTT client it trying to do.
//
class sample_mem_persistence : virtual public mqtt::iclient_persistence
{
// Whether the store is open
bool open_;
// Use an STL map to store shared persistence pointers
// against string keys.
std::map<std::string, std::string> store_;
public:
sample_mem_persistence() : open_(false) {}
// "Open" the store
void open(const std::string& clientId, const std::string& serverURI) override
{
std::cout << " [Opening persistence store for '" << clientId << "' at '" << serverURI
<< "']" << std::endl;
open_ = true;
}
// Close the persistent store that was previously opened.
void close() override
{
std::cout << " [Closing persistence store.]" << std::endl;
open_ = false;
}
// Clears persistence, so that it no longer contains any persisted data.
void clear() override
{
std::cout << " [Clearing persistence store.]" << std::endl;
store_.clear();
}
// Returns whether or not data is persisted using the specified key.
bool contains_key(const std::string& key) override
{
return store_.find(key) != store_.end();
}
// Returns the keys in this persistent data store.
mqtt::string_collection keys() const override
{
mqtt::string_collection ks;
for (const auto& k : store_) ks.push_back(k.first);
return ks;
}
// Puts the specified data into the persistent store.
void put(const std::string& key, const std::vector<mqtt::string_view>& bufs) override
{
std::cout << " [Persisting data with key '" << key << "']" << std::endl;
std::string str;
for (const auto& b : bufs) str.append(b.data(), b.size()); // += b.str();
store_[key] = std::move(str);
}
// Gets the specified data out of the persistent store.
std::string get(const std::string& key) const override
{
std::cout << " [Searching persistence for key '" << key << "']" << std::endl;
auto p = store_.find(key);
if (p == store_.end())
throw mqtt::persistence_exception();
std::cout << " [Found persistence data for key '" << key << "']" << std::endl;
return p->second;
}
// Remove the data for the specified key.
void remove(const std::string& key) override
{
std::cout << " [Persistence removing key '" << key << "']" << std::endl;
auto p = store_.find(key);
if (p == store_.end())
throw mqtt::persistence_exception();
store_.erase(p);
std::cout << " [Persistence key removed '" << key << "']" << std::endl;
}
};
/////////////////////////////////////////////////////////////////////////////
// Class to receive callbacks
class user_callback : public virtual mqtt::callback
{
void connection_lost(const std::string& cause) override
{
std::cout << "\nConnection lost" << std::endl;
if (!cause.empty())
std::cout << "\tcause: " << cause << std::endl;
}
void delivery_complete(mqtt::delivery_token_ptr tok) override
{
std::cout << "\n [Delivery complete for token: "
<< (tok ? tok->get_message_id() : -1) << "]" << std::endl;
}
public:
};
// --------------------------------------------------------------------------
int main(int argc, char* argv[])
{
auto serverURI = (argc > 1) ? std::string{argv[1]} : DFLT_SERVER_URI;
std::cout << "Initializing..." << std::endl;
sample_mem_persistence persist;
mqtt::client client(serverURI, "", &persist);
user_callback cb;
client.set_callback(cb);
mqtt::connect_options connOpts;
connOpts.set_keep_alive_interval(20);
connOpts.set_clean_session(true);
std::cout << "...OK" << std::endl;
try {
std::cout << "\nConnecting..." << std::endl;
client.connect(connOpts);
std::cout << "...OK" << std::endl;
// First use a message pointer.
std::cout << "\nSending message..." << std::endl;
auto pubmsg = mqtt::make_message(TOPIC, PAYLOAD1);
pubmsg->set_qos(QOS);
client.publish(pubmsg);
std::cout << "...OK" << std::endl;
// Now try with itemized publish.
std::cout << "\nSending next message..." << std::endl;
client.publish(TOPIC, PAYLOAD2, strlen(PAYLOAD2) + 1);
std::cout << "...OK" << std::endl;
// Now try with a listener, no token, and non-heap message
std::cout << "\nSending final message..." << std::endl;
client.publish(mqtt::message(TOPIC, PAYLOAD3, QOS, false));
std::cout << "OK" << std::endl;
// Disconnect
std::cout << "\nDisconnecting..." << std::endl;
client.disconnect();
std::cout << "...OK" << std::endl;
}
catch (const mqtt::persistence_exception& exc) {
std::cerr << "Persistence Error: " << exc.what() << " [" << exc.get_reason_code()
<< "]" << std::endl;
return 1;
}
catch (const mqtt::exception& exc) {
std::cerr << exc.what() << std::endl;
return 1;
}
std::cout << "\nExiting" << std::endl;
return 0;
}

129
examples/sync_reconnect.cpp Normal file
View File

@ -0,0 +1,129 @@
// sync_reconnect.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// It's a fairly contrived, but useful example of an MQTT data monitor and
// publisher, using the C++ synchronous client interface. A fairly common
// usage for MQTT applications to stay offline for much of the time and only
// connect to the broker when there is data to send.
//
// Since we don't have a universal sensor to use for this example, we simply
// use time itself as out input data. We periodically "sample" the time
// value, connect, send the value, disconnect, and then sleep. In this case
// we use the system clock, measuring the time with millisecond precision.
//
// The sample demonstrates:
// - The synchronous client
// - Connecting to an MQTT server/broker
// - Periodically reconnecting to the broker
// - Publishing messages using a `topic` object
// - Using `connect_options` with builder classes
//
/*******************************************************************************
* Copyright (c) 2019-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/client.h"
using namespace std;
using namespace std::chrono;
const std::string DFLT_SERVER_URI{"mqtt://localhost:1883"};
// The QoS for sending data
const int QOS = 1;
// How often to sample the "data"
const auto SAMPLE_PERIOD = seconds(5);
// --------------------------------------------------------------------------
// Gets the current time as the number of milliseconds since the epoch:
// like a time_t with ms resolution.
uint64_t timestamp()
{
auto now = system_clock::now();
auto tse = now.time_since_epoch();
auto msTm = duration_cast<milliseconds>(tse);
return uint64_t(msTm.count());
}
// --------------------------------------------------------------------------
int main(int argc, char* argv[])
{
// The server URI (address)
string serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI;
// The amount of time to run (in sec). Zero means "run forever".
uint64_t trun = (argc > 2) ? stoll(argv[2]) : 0LL;
cout << "Initializing for server '" << serverURI << "'..." << endl;
mqtt::client cli(serverURI, "");
auto connOpts = mqtt::connect_options_builder().clean_session().finalize();
cli.set_timeout(seconds(3));
auto top = cli.get_topic("data/time", QOS);
uint64_t t = timestamp(), tstart = t;
try {
// We need to connect once before we can use reconnect()
cli.connect(connOpts);
while (true) {
cout << "\nCollecting data..." << endl;
// Collect some data
t = timestamp();
if (!cli.is_connected()) {
cout << "Reconnecting..." << endl;
cli.reconnect();
}
cout << "Publishing data: " << t << "..." << endl;
top.publish(to_string(t));
cout << "Disconnecting..." << endl;
cli.disconnect();
// Quit if it's past time
if (trun > 0 && t >= (trun + tstart))
break;
cout << "Going to sleep." << endl;
this_thread::sleep_for(SAMPLE_PERIOD);
}
}
catch (const mqtt::exception& exc) {
cerr << exc << endl;
return 1;
}
return 0;
}

View File

@ -13,14 +13,14 @@
// //
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2019 Frank Pagliughi <fpagliughi@mindspring.com> * Copyright (c) 2019-2023 Frank Pagliughi <fpagliughi@mindspring.com>
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution. * and Eclipse Distribution License v1.0 which accompany this distribution.
* *
* The Eclipse Public License is available at * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php. * http://www.eclipse.org/org/documents/edl-v10.php.
* *
@ -28,28 +28,26 @@
* Frank Pagliughi - initial implementation and documentation * Frank Pagliughi - initial implementation and documentation
*******************************************************************************/ *******************************************************************************/
#include <iostream>
#include <cstdlib>
#include <string>
#include <thread> // For sleep
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <cstdlib>
#include <cstring> #include <cstring>
#include <iostream>
#include <string>
#include <thread> // For sleep
#include "mqtt/async_client.h" #include "mqtt/async_client.h"
using namespace std; using namespace std;
const string DFLT_SERVER_ADDRESS { "tcp://localhost:1883" }; const string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const string TOPIC { "test" }; const string TOPIC{"test"};
const int QOS = 1; const int QOS = 1;
const char* PAYLOADS[] = { const char* PAYLOADS[] = {
"Hello World!", "Hello World!", "Hi there!", "Is anyone listening?", "Someone is always listening.",
"Hi there!", nullptr
"Is anyone listening?",
"Someone is always listening.",
nullptr
}; };
const auto TIMEOUT = std::chrono::seconds(10); const auto TIMEOUT = std::chrono::seconds(10);
@ -58,40 +56,39 @@ const auto TIMEOUT = std::chrono::seconds(10);
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
string address = (argc > 1) ? string(argv[1]) : DFLT_SERVER_ADDRESS; string serverURI = (argc > 1) ? string(argv[1]) : DFLT_SERVER_URI;
cout << "Initializing for server '" << address << "'..." << endl; cout << "Initializing for server '" << serverURI << "'..." << endl;
mqtt::async_client cli(address, ""); mqtt::async_client cli(serverURI, "");
cout << " ...OK" << endl; cout << " ...OK" << endl;
try { try {
cout << "\nConnecting..." << endl; cout << "\nConnecting..." << endl;
cli.connect()->wait(); cli.connect()->wait();
cout << " ...OK" << endl; cout << " ...OK" << endl;
cout << "\nPublishing messages..." << endl; cout << "\nPublishing messages..." << endl;
mqtt::topic top(cli, "test", QOS); mqtt::topic top(cli, "test", QOS);
mqtt::token_ptr tok; mqtt::token_ptr tok;
size_t i = 0; size_t i = 0;
while (PAYLOADS[i]) { while (PAYLOADS[i]) {
tok = top.publish(PAYLOADS[i++]); tok = top.publish(PAYLOADS[i++]);
} }
tok->wait(); // Just wait for the last one to complete. tok->wait(); // Just wait for the last one to complete.
cout << "OK" << endl; cout << "OK" << endl;
// Disconnect // Disconnect
cout << "\nDisconnecting..." << endl; cout << "\nDisconnecting..." << endl;
cli.disconnect()->wait(); cli.disconnect()->wait();
cout << " ...OK" << endl; cout << " ...OK" << endl;
} }
catch (const mqtt::exception& exc) { catch (const mqtt::exception& exc) {
cerr << exc.what() << endl; cerr << exc << endl;
return 1; return 1;
} }
return 0; return 0;
} }

107
examples/ws_publish.cpp Normal file
View File

@ -0,0 +1,107 @@
// ws_publish.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// It's an example of how to connect to an MQTT broker using websockets with
// an optional proxy.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker using websockets
// - Publishing messages
// - Using asynchronous tokens
//
// This example requires a broker that is configured to accept websocket
// connections, and optionally, an HTTP proxy.
//
/*******************************************************************************
* Copyright (c) 2020-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include "mqtt/async_client.h"
// Assume a local server with websocket support on port 8080
const std::string DFLT_SERVER_URI{"ws://localhost:8080"};
// A local proxy, like squid on port 3128
// Here assuming basic authentication with user "user" and password "pass".
const std::string DFLT_PROXY_ADDRESS{"http://user:pass@localhost:3128"};
// Quality of service for this app.
const int QOS = 1;
// Timeout for publish to complete
const auto TIMEOUT = std::chrono::seconds(10);
using namespace std;
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
string serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI,
proxy = (argc > 2) ? string{argv[2]} : DFLT_PROXY_ADDRESS;
cout << "Initializing for server '" << serverURI << "'..." << endl;
if (!proxy.empty())
cout << " with proxy '" << proxy << "'" << endl;
mqtt::async_client client(serverURI, "");
// Build the connect options.
auto connBuilder = mqtt::connect_options_builder::ws();
if (!proxy.empty())
connBuilder.http_proxy(proxy);
auto connOpts = connBuilder.keep_alive_interval(std::chrono::seconds(45)).finalize();
cout << " ...OK" << endl;
try {
// Connect to the server
cout << "\nConnecting..." << endl;
client.connect(connOpts)->wait();
cout << " ...OK" << endl;
// Send a message
cout << "\nSending message..." << endl;
auto msg = mqtt::make_message("hello", "Hello C++ websocket world!", QOS, false);
bool ok = client.publish(msg)->wait_for(TIMEOUT);
cout << " ..." << (ok ? "OK" : "Error") << endl;
// Disconnect
cout << "\nDisconnecting..." << endl;
client.disconnect()->wait();
cout << " ...OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.get_error_str() << endl;
return 1;
}
return 0;
}

1
externals/paho-mqtt-c vendored Submodule

@ -0,0 +1 @@
Subproject commit 2150ba29d9df24ad1733c460eb099f292af84ee5

8
fmt.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
#
# Runs clang format over the whole project tree, excluding
# the 'externals/' and 'build/' directories.
#
find . -type d \( -path './externals' -o -path './build' \) -prune -iname '*.h' -o -iname '*.cpp' | xargs clang-format -i

273
include/.clang-format Normal file
View File

@ -0,0 +1,273 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -4
#AlignAfterOpenBracket: Align
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
# Clang-17
#AlignConsecutiveShortCaseStatements:
# Enabled: false
# AcrossEmptyLines: false
# AcrossComments: false
# AlignCaseColons: false
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterAttributes: Never
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Custom
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 94
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: BeforeHash
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: true
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
KeepEmptyLinesAtEOF: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: NextLine
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
PPIndentWidth: -1
QualifierAlignment: Leave
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: pb
BasedOnStyle: google
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
# Clang Format >v17
#SpacesInParens: Never
#SpacesInParensOptions:
# InCStyleCasts: false
# InConditionalStatements: false
# InEmptyParentheses: false
# Other: false
SpacesInSquareBrackets: false
Standard: Auto
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...

View File

@ -1,18 +1,18 @@
#******************************************************************************* #*******************************************************************************
# Copyright (c) 2016-2018 # Copyright (c) 2016-2024 Frank Pagliughi <fpagliughi@mindspring.com>
# #
# All rights reserved. This program and the accompanying materials # All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0 # are made available under the terms of the Eclipse Public License v2.0
# and Eclipse Distribution License v1.0 which accompany this distribution. # and Eclipse Distribution License v1.0 which accompany this distribution.
# #
# The Eclipse Public License is available at # The Eclipse Public License is available at
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v20.html
# and the Eclipse Distribution License is available at # and the Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php. # http://www.eclipse.org/org/documents/edl-v10.php.
# #
# Contributors: # Contributors:
# Guilherme Maciel Ferreira - initial version # Guilherme Maciel Ferreira - initial version
# Frank Pagliughi # Frank Pagliughi - updates throughout
#*******************************************************************************/ #*******************************************************************************/
install( install(
@ -23,21 +23,27 @@ install(
callback.h callback.h
client.h client.h
connect_options.h connect_options.h
create_options.h
delivery_token.h delivery_token.h
disconnect_options.h disconnect_options.h
event.h
exception.h exception.h
export.h
iaction_listener.h iaction_listener.h
iasync_client.h iasync_client.h
iclient_persistence.h iclient_persistence.h
message.h message.h
properties.h platform.h
properties.h
reason_code.h
response_options.h response_options.h
server_response.h server_response.h
ssl_options.h ssl_options.h
string_collection.h string_collection.h
subscribe_options.h subscribe_options.h
thread_queue.h thread_queue.h
token.h token.h
topic_matcher.h
topic.h topic.h
types.h types.h
will_options.h will_options.h

1036
include/mqtt/async_client.h Normal file

File diff suppressed because it is too large Load Diff

310
include/mqtt/buffer_ref.h Normal file
View File

@ -0,0 +1,310 @@
/////////////////////////////////////////////////////////////////////////////
/// @file buffer_ref.h
/// Buffer reference type for the Paho MQTT C++ library.
/// @date April 18, 2017
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2017-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_buffer_ref_h
#define __mqtt_buffer_ref_h
#include <cstring>
#include <iostream>
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* A reference object for holding immutable data buffers, with cheap copy
* semantics and lifetime management.
*
* Each object of this class contains a reference-counted pointer to an
* immutable data buffer. Objects can be copied freely and easily, even
* across threads, since all instances promise not to modify the contents
* of the buffer.
*
* The buffer is immutable but the reference itself acts like a normal
* variable. It can be reassigned to point to a different buffer.
*
* If no value has been assigned to a reference, then it is in a default
* "null" state. It is not safe to call any member functions on a null
* reference, other than to check if the object is null or empty.
* @verbatim
* string_ref sr;
* if (!sr)
* cout << "null reference" << endl;
* else
* cout.write(sr.data(), sr.size());
* @endverbatim
*/
template <typename T>
class buffer_ref
{
public:
/**
* The underlying type for the buffer.
* Normally byte-wide data (char or uint8_t) for Paho.
*/
using value_type = T;
/**
* The type for the buffer.
* We use basic_string for compatibility with string data.
*/
using blob = std::basic_string<value_type>;
/**
* The pointer we use.
* Note that it is a pointer to a _const_ blob.
*/
using pointer_type = std::shared_ptr<const blob>;
private:
/** Our data is a shared pointer to a const buffer */
pointer_type data_;
public:
/**
* Default constructor creates a null reference.
*/
buffer_ref() = default;
/**
* Copy constructor only copies a shared pointer.
* @param buf Another buffer reference.
*/
buffer_ref(const buffer_ref& buf) = default;
/**
* Move constructor only moves a shared pointer.
* @param buf Another buffer reference.
*/
buffer_ref(buffer_ref&& buf) = default;
/**
* Creates a reference to a new buffer by copying data.
* @param b A string from which to create a new buffer.
*/
buffer_ref(const blob& b) : data_{std::make_shared<blob>(b)} {}
/**
* Creates a reference to a new buffer by moving a string into the
* buffer.
* @param b A string from which to create a new buffer.
*/
buffer_ref(blob&& b) : data_{std::make_shared<blob>(std::move(b))} {}
/**
* Creates a reference to an existing buffer by copying the shared
* pointer.
* Note that it is up to the caller to insure that there are no mutable
* references to the buffer.
* @param p A shared pointer to a string.
*/
buffer_ref(const pointer_type& p) : data_(p) {}
/**
* Creates a reference to an existing buffer by moving the shared
* pointer.
* Note that it is up to the caller to insure that there are no mutable
* references to the buffer.
* @param p A shared pointer to a string.
*/
buffer_ref(pointer_type&& p) : data_(std::move(p)) {}
/**
* Creates a reference to a new buffer containing a copy of the data.
* @param buf The memory to copy
* @param n The number of bytes to copy.
*/
buffer_ref(const value_type* buf, size_t n) : data_{std::make_shared<blob>(buf, n)} {}
/**
* Creates a reference to a new buffer containing a copy of the
* NUL-terminated char array.
* @param buf A NUL-terminated char array (C string).
*/
buffer_ref(const char* buf)
: buffer_ref(reinterpret_cast<const value_type*>(buf), std::strlen(buf)) {
static_assert(
sizeof(char) == sizeof(T), "can only use C arr with char or byte buffers"
);
}
/**
* Copy the reference to the buffer.
* @param rhs Another buffer
* @return A reference to this object
*/
buffer_ref& operator=(const buffer_ref& rhs) = default;
/**
* Move a reference to a buffer.
* @param rhs The other reference to move.
* @return A reference to this object.
*/
buffer_ref& operator=(buffer_ref&& rhs) = default;
/**
* Copy a string into this object, creating a new buffer.
* Modifies the reference for this object, pointing it to a
* newly-created buffer. Other references to the old object remain
* unchanges, so this follows copy-on-write semantics.
* @param b A new blob/string to copy.
* @return A reference to this object.
*/
buffer_ref& operator=(const blob& b) {
data_.reset(new blob(b));
return *this;
}
/**
* Move a string into this object, creating a new buffer.
* Modifies the reference for this object, pointing it to a
* newly-created buffer. Other references to the old object remain
* unchanges, so this follows copy-on-write semantics.
* @param b A new blob/string to move.
* @return A reference to this object.
*/
buffer_ref& operator=(blob&& b) {
data_.reset(new blob(std::move(b)));
return *this;
}
/**
* Copy a NUL-terminated C char array into a new buffer
* @param cstr A NUL-terminated C string.
* @return A reference to this object
*/
buffer_ref& operator=(const char* cstr) {
static_assert(
sizeof(char) == sizeof(T), "can only use C arr with char or byte buffers"
);
data_.reset(new blob(reinterpret_cast<const value_type*>(cstr), strlen(cstr)));
return *this;
}
/**
* Copy another type of buffer reference to this one.
* This can copy a buffer of different types, provided that the size of
* the data elements are the same. This is typically used to convert
* from char to byte, where the data is the same, but the interpretation
* is different. Note that this copies the underlying buffer.
* @param rhs A reference to a different type of buffer.
* @return A reference to this object.
*/
template <typename OT>
buffer_ref& operator=(const buffer_ref<OT>& rhs) {
static_assert(
sizeof(OT) == sizeof(T), "Can only assign buffers if values the same size"
);
data_.reset(new blob(reinterpret_cast<const value_type*>(rhs.data()), rhs.size()));
return *this;
}
/**
* Clears the reference to nil.
*/
void reset() { data_.reset(); }
/**
* Determines if the reference is valid.
* If the reference is invalid then it is not safe to call @em any
* member functions other than @ref is_null() and @ref empty()
* @return @em true if referring to a valid buffer, @em false if the
* reference (pointer) is null.
*/
explicit operator bool() const { return bool(data_); }
/**
* Determines if the reference is invalid.
* If the reference is invalid then it is not safe to call @em any
* member functions other than @ref is_null() and @ref empty()
* @return @em true if the reference is null, @em false if it is
* referring to a valid buffer,
*/
bool is_null() const { return !data_; }
/**
* Determines if the buffer is empty.
* @return @em true if the buffer is empty or the reference is null,
* @em false if the buffer contains data.
*/
bool empty() const { return !data_ || data_->empty(); }
/**
* Gets a const pointer to the data buffer.
* @return A pointer to the data buffer.
*/
const value_type* data() const { return data_->data(); }
/**
* Gets the size of the data buffer.
* @return The size of the data buffer.
*/
size_t size() const { return data_->size(); }
/**
* Gets the size of the data buffer.
* @return The size of the data buffer.
*/
size_t length() const { return data_->length(); }
/**
* Gets the data buffer as a string.
* @return The data buffer as a string.
*/
const blob& str() const { return *data_; }
/**
* Gets the data buffer as a string.
* @return The data buffer as a string.
*/
const blob& to_string() const { return str(); }
/**
* Gets the data buffer as NUL-terminated C string.
* Note that the reference must be set to call this function.
* @return The data buffer as a string.
*/
const char* c_str() const { return data_->c_str(); }
/**
* Gets a shared pointer to the (const) data buffer.
* @return A shared pointer to the (const) data buffer.
*/
const pointer_type& ptr() const { return data_; }
/**
* Gets elemental access to the data buffer (read only)
* @param i The index into the buffer.
* @return The value at the specified index.
*/
const value_type& operator[](size_t i) const { return (*data_)[i]; }
};
/**
* Stream inserter for a buffer reference.
* This does a binary write of the data in the buffer.
* @param os The output stream.
* @param buf The buffer reference to write.
* @return A reference to the output stream.
*/
template <typename T>
std::ostream& operator<<(std::ostream& os, const buffer_ref<T>& buf) {
if (!buf.empty())
os.write(buf.data(), buf.size());
return os;
}
/////////////////////////////////////////////////////////////////////////////
/**
* A reference to a text buffer.
*/
using string_ref = buffer_ref<char>;
/**
* A reference to a binary buffer.
* Note that we're using char for the underlying data type to allow
* efficient moves to and from std::string's. Using a separate type
* indicates that the data may be arbitrary binary.
*/
using binary_ref = buffer_ref<char>;
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_buffer_ref_h

134
include/mqtt/buffer_view.h Normal file
View File

@ -0,0 +1,134 @@
/////////////////////////////////////////////////////////////////////////////
/// @file buffer_view.h
/// Buffer reference type for the Paho MQTT C++ library.
/// @date April 18, 2017
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2017-2020 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_buffer_view_h
#define __mqtt_buffer_view_h
#include <iostream>
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* A reference to a contiguous sequence of items, with no ownership.
* This simply contains a pointer to a const array of items, and a size.
* This is a similar, but simplified version of the std::string_view
* class(es) in the C++17 standard.
*/
template <typename T>
class buffer_view
{
public:
/** The type of items to be held in the queue. */
using value_type = T;
/** The type used to specify number of items in the container. */
using size_type = size_t;
private:
/** Const pointer to the data array */
const value_type* data_;
/** The size of the array */
size_type sz_;
public:
/**
* Constructs a buffer view.
* @param data The data pointer
* @param n The number of items
*/
buffer_view(const value_type* data, size_type n) : data_(data), sz_(n) {}
/**
* Constructs a buffer view to a whole string.
* This the starting pointer and length of the whole string.
* @param str The string.
*/
buffer_view(const std::basic_string<value_type>& str)
: data_(str.data()), sz_(str.size()) {}
/**
* Gets a pointer the first item in the view.
* @return A const pointer the first item in the view.
*/
const value_type* data() const { return data_; }
/**
* Gets the number of items in the view.
* @return The number of items in the view.
*/
size_type size() const { return sz_; }
/**
* Gets the number of items in the view.
* @return The number of items in the view.
*/
size_type length() const { return sz_; }
/**
* Access an item in the view.
* @param i The index of the item.
* @return A const reference to the requested item.
*/
const value_type& operator[](size_t i) const { return data_[i]; }
/**
* Gets a copy of the view as a string.
* @return A copy of the view as a string.
*/
std::basic_string<value_type> str() const {
return std::basic_string<value_type>(data_, sz_);
}
/**
* Gets a copy of the view as a string.
* @return A copy of the view as a string.
*/
string to_string() const {
static_assert(
sizeof(char) == sizeof(T), "can only get string for char or byte buffers"
);
return string(reinterpret_cast<const char*>(data_), sz_);
}
};
/**
* Stream inserter for a buffer view.
* This does a binary write of the data in the buffer.
* @param os The output stream.
* @param buf The buffer reference to write.
* @return A reference to the output stream.
*/
template <typename T>
std::ostream& operator<<(std::ostream& os, const buffer_view<T>& buf) {
if (buf.size() > 0)
os.write(buf.data(), sizeof(T) * buf.size());
return os;
}
/** A buffer view for character string data. */
using string_view = buffer_view<char>;
/** A buffer view for binary data */
using binary_view = buffer_view<char>;
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
} // namespace mqtt
#endif // __mqtt_buffer_view_h

View File

@ -9,11 +9,11 @@
* Copyright (c) 2013-2019 Frank Pagliughi <fpagliughi@mindspring.com> * Copyright (c) 2013-2019 Frank Pagliughi <fpagliughi@mindspring.com>
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution. * and Eclipse Distribution License v1.0 which accompany this distribution.
* *
* The Eclipse Public License is available at * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php. * http://www.eclipse.org/org/documents/edl-v10.php.
* *
@ -24,11 +24,12 @@
#ifndef __mqtt_callback_h #ifndef __mqtt_callback_h
#define __mqtt_callback_h #define __mqtt_callback_h
#include <memory>
#include <vector>
#include "MQTTAsync.h" #include "MQTTAsync.h"
#include "mqtt/delivery_token.h" #include "mqtt/delivery_token.h"
#include "mqtt/types.h" #include "mqtt/types.h"
#include <vector>
#include <memory>
namespace mqtt { namespace mqtt {
@ -41,40 +42,36 @@ namespace mqtt {
class callback class callback
{ {
public: public:
/** Smart/shared pointer to an object of this type */ /** Smart/shared pointer to an object of this type */
using ptr_t = std::shared_ptr<callback>; using ptr_t = std::shared_ptr<callback>;
/** Smart/shared pointer to a const object of this type */ /** Smart/shared pointer to a const object of this type */
using const_ptr_t = std::shared_ptr<const callback>; using const_ptr_t = std::shared_ptr<const callback>;
/** /**
* Virtual destructor. * Virtual destructor.
*/ */
virtual ~callback() {} virtual ~callback() {}
/** /**
* This method is called when the client is connected. * This method is called when the client is connected.
* Note that, in response to an initial connect(), the token from the * Note that, in response to an initial connect(), the token from the
* connect call is also signaled with an on_success(). That occurs just * connect call is also signaled with an on_success(). That occurs just
* before this is called. * before this is called.
* @param cause */
*/ virtual void connected(const string& /*cause*/) {}
virtual void connected(const string& /*cause*/) {} /**
/** * This method is called when the connection to the server is lost.
* This method is called when the connection to the server is lost. */
* @param cause virtual void connection_lost(const string& /*cause*/) {}
*/ /**
virtual void connection_lost(const string& /*cause*/) {} * This method is called when a message arrives from the server.
/** */
* This method is called when a message arrives from the server. virtual void message_arrived(const_message_ptr /*msg*/) {}
* @param msg The message /**
*/ * Called when delivery for a message has been completed, and all
virtual void message_arrived(const_message_ptr /*msg*/) {} * acknowledgments have been received.
/** */
* Called when delivery for a message has been completed, and all virtual void delivery_complete(delivery_token_ptr /*tok*/) {}
* acknowledgments have been received.
* @param tok The token tracking the message delivery.
*/
virtual void delivery_complete(delivery_token_ptr /*tok*/) {}
}; };
/** Smart/shared pointer to a callback object */ /** Smart/shared pointer to a callback object */
@ -84,8 +81,6 @@ using callback_ptr = callback::ptr_t;
using const_callback_ptr = callback::const_ptr_t; using const_callback_ptr = callback::const_ptr_t;
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// end namespace mqtt } // namespace mqtt
}
#endif // __mqtt_callback_h
#endif // __mqtt_callback_h

429
include/mqtt/client.h Normal file
View File

@ -0,0 +1,429 @@
/////////////////////////////////////////////////////////////////////////////
/// @file client.h
/// Declaration of MQTT client class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_client_h
#define __mqtt_client_h
#include <future>
#include "mqtt/async_client.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Lightweight client for talking to an MQTT server using methods that block
* until an operation completes.
*/
class client : private callback
{
/** An arbitrary, but relatively long timeout */
PAHO_MQTTPP_EXPORT static const std::chrono::seconds DFLT_TIMEOUT;
/** The default quality of service */
PAHO_MQTTPP_EXPORT static const int DFLT_QOS; // =1;
/** The actual client */
async_client cli_;
/** The longest time to wait for an operation to complete. */
std::chrono::milliseconds timeout_;
/** Callback supplied by the user (if any) */
callback* userCallback_;
/**
* Creates a shared pointer to an existing non-heap object.
* The shared pointer is given a no-op deleter, so it will not try to
* destroy the object when it goes out of scope. It is up to the caller
* to ensure that the object remains in memory for as long as there may
* be pointers to it.
* @param val A value which may live anywhere in memory (stack,
* file-scope, etc).
* @return A shared pointer to the object.
*/
template <typename T>
std::shared_ptr<T> ptr(const T& val) {
return std::shared_ptr<T>(const_cast<T*>(&val), [](T*) {});
}
// User callbacks
// Most are launched in a separate thread, for convenience, except
// message_arrived, for performance.
void connected(const string& cause) override {
std::async(std::launch::async, &callback::connected, userCallback_, cause).wait();
}
void connection_lost(const string& cause) override {
std::async(std::launch::async, &callback::connection_lost, userCallback_, cause)
.wait();
}
void message_arrived(const_message_ptr msg) override {
userCallback_->message_arrived(msg);
}
void delivery_complete(delivery_token_ptr tok) override {
std::async(std::launch::async, &callback::delivery_complete, userCallback_, tok)
.wait();
}
/** Non-copyable */
client() = delete;
client(const async_client&) = delete;
client& operator=(const async_client&) = delete;
public:
/** Smart pointer type for this object */
using ptr_t = std::shared_ptr<client>;
/** Type for a collection of QOS values */
using qos_collection = async_client::qos_collection;
/** Handler for updating connection data before an auto-reconnect. */
using update_connection_handler = async_client::update_connection_handler;
/**
* Create a client that can be used to communicate with an MQTT server.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to.
* @param persistence The desired persistence type.
*/
client(
const string& serverURI, const string& clientId = string{},
const persistence_type& persistence = NO_PERSISTENCE
);
/**
* Create a client that can be used to communicate with an MQTT server,
* which allows for off-line message buffering.
* This allows the caller to specify a user-defined persistence object,
* or use no persistence.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param maxBufferedMessages the maximum number of messages allowed to
* be buffered while not connected
* @param persistence The desired persistence type.
*/
client(
const string& serverURI, const string& clientId, int maxBufferedMessages,
const persistence_type& persistence = NO_PERSISTENCE
);
/**
* Create a client that can be used to communicate with an MQTT server,
* which allows for off-line message buffering. This allows the caller
* to specify a user-defined persistence object, or use no persistence.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param opts The create options
* @param persistence The desired persistence type.
*/
client(
const string& serverURI, const string& clientId, const create_options& opts,
const persistence_type& persistence = NO_PERSISTENCE
);
/**
* Create a client that can be used to communicate with an MQTT server.
* @param opts The create options
*/
client(const create_options& opts);
/**
* Virtual destructor
*/
virtual ~client() {}
/**
* Connects to an MQTT server using the default options.
*/
virtual connect_response connect();
/**
* Connects to an MQTT server using the specified options.
* @param opts The connect options
*/
virtual connect_response connect(connect_options opts);
/**
* Reconnects the client using options from the previous connect.
* The client must have previously called connect() for this to work.
*/
virtual connect_response reconnect();
/**
* Disconnects from the server.
*/
virtual void disconnect();
/**
* Disconnects from the server.
* @param timeoutMS the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
*/
virtual void disconnect(int timeoutMS);
/**
* Disconnects from the server.
* @param to the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
*/
template <class Rep, class Period>
void disconnect(const std::chrono::duration<Rep, Period>& to) {
disconnect((int)to_milliseconds_count(to));
}
/**
* Gets the client ID used by this client.
* @return The client ID used by this client.
*/
virtual string get_client_id() const { return cli_.get_client_id(); }
/**
* Gets the address of the server used by this client.
* @return The address of the server used by this client, as a URI.
*/
virtual string get_server_uri() const { return cli_.get_server_uri(); }
/**
* Return the maximum time to wait for an action to complete.
* @return int
*/
virtual std::chrono::milliseconds get_timeout() const { return timeout_; }
/**
* Gets a copy of the connect options that were last used in a request
* to connect to the broker.
* @returns The last connect options that were used.
*/
connect_options get_connect_options() const { return cli_.get_connect_options(); }
/**
* Get a topic object which can be used to publish messages on this
* client.
* @param top The topic name
* @param qos The Quality of Service for the topic
* @param retained Whether the published messages set the retain flag.
* @return A topic attached to this client.
*/
virtual topic get_topic(
const string& top, int qos = message::DFLT_QOS, bool retained = message::DFLT_RETAINED
) {
return topic(cli_, top, qos, retained);
}
/**
* Determines if this client is currently connected to the server.
* @return @em true if the client is currently connected, @em false if
* not.
*/
virtual bool is_connected() const { return cli_.is_connected(); }
/**
* Sets a callback to allow the application to update the connection
* data on automatic reconnects.
* @param cb The callback functor to register with the library.
*/
void set_update_connection_handler(update_connection_handler cb) {
cli_.set_update_connection_handler(cb);
}
/**
* Publishes a message to a topic on the server and return once it is
* delivered.
* @param top The topic to publish
* @param payload The data to publish
* @param n The size in bytes of the data
* @param qos The QoS for message delivery
* @param retained Whether the broker should retain the message
*/
virtual void publish(
string_ref top, const void* payload, size_t n, int qos, bool retained
) {
if (!cli_.publish(std::move(top), payload, n, qos, retained)->wait_for(timeout_))
throw timeout_error();
}
/**
* Publishes a message to a topic on the server and return once it is
* delivered.
* @param top The topic to publish
* @param payload The data to publish
* @param n The size in bytes of the data
*/
virtual void publish(string_ref top, const void* payload, size_t n) {
if (!cli_.publish(std::move(top), payload, n)->wait_for(timeout_))
throw timeout_error();
}
/**
* Publishes a message to a topic on the server.
* @param msg The message
*/
virtual void publish(const_message_ptr msg) {
if (!cli_.publish(msg)->wait_for(timeout_))
throw timeout_error();
}
/**
* Publishes a message to a topic on the server.
* This version will not timeout since that could leave the library with
* a reference to memory that could disappear while the library is still
* using it.
* @param msg The message
*/
virtual void publish(const message& msg) { cli_.publish(ptr(msg))->wait(); }
/**
* Sets the callback listener to use for events that happen
* asynchronously.
* @param cb The callback functions
*/
virtual void set_callback(callback& cb);
/**
* Set the maximum time to wait for an action to complete.
* @param timeoutMS The timeout in milliseconds
*/
virtual void set_timeout(int timeoutMS) {
timeout_ = std::chrono::milliseconds(timeoutMS);
}
/**
* Set the maximum time to wait for an action to complete.
* @param to The timeout as a std::chrono duration.
*/
template <class Rep, class Period>
void set_timeout(const std::chrono::duration<Rep, Period>& to) {
timeout_ = to_milliseconds(to);
}
/**
* Subscribe to a topic, which may include wildcards using a QoS of 1.
* @param topicFilter A single topic to subscribe
maked * @param props The MQTT v5 properties.
* @param opts The MQTT v5 subscribe options for the topic
* @return The "subscribe" response from the server.
*/
virtual subscribe_response subscribe(
const string& topicFilter, const subscribe_options& opts = subscribe_options(),
const properties& props = properties()
);
/**
* Subscribe to a topic, which may include wildcards.
* @param topicFilter A single topic to subscribe
* @param qos The QoS of the subscription
* @param opts The MQTT v5 subscribe options for the topic
* @param props The MQTT v5 properties.
* @return The "subscribe" response from the server.
*/
virtual subscribe_response subscribe(
const string& topicFilter, int qos,
const subscribe_options& opts = subscribe_options(),
const properties& props = properties()
);
/**
* Subscribes to a one or more topics, which may include wildcards using
* a QoS of 1.
* @param topicFilters A set of topics to subscribe
* @param opts The MQTT v5 subscribe options (one for each topic)
* @param props The MQTT v5 properties.
* @return The "subscribe" response from the server.
*/
virtual subscribe_response subscribe(
const string_collection& topicFilters,
const std::vector<subscribe_options>& opts = std::vector<subscribe_options>(),
const properties& props = properties()
);
/**
* Subscribes to multiple topics, each of which may include wildcards.
* @param topicFilters A collection of topics to subscribe
* @param qos A collection of QoS for each topic
* @param opts The MQTT v5 subscribe options (one for each topic)
* @param props The MQTT v5 properties.
* @return The "subscribe" response from the server.
*/
virtual subscribe_response subscribe(
const string_collection& topicFilters, const qos_collection& qos,
const std::vector<subscribe_options>& opts = std::vector<subscribe_options>(),
const properties& props = properties()
);
/**
* Requests the server unsubscribe the client from a topic.
* @param topicFilter A single topic to unsubscribe.
* @param props The MQTT v5 properties.
* @return The "unsubscribe" response from the server.
*/
virtual unsubscribe_response unsubscribe(
const string& topicFilter, const properties& props = properties()
);
/**
* Requests the server unsubscribe the client from one or more topics.
* @param topicFilters A collection of topics to unsubscribe.
* @param props The MQTT v5 properties.
* @return The "unsubscribe" response from the server.
*/
virtual unsubscribe_response unsubscribe(
const string_collection& topicFilters, const properties& props = properties()
);
/**
* Start consuming messages.
* This initializes the client to receive messages through a queue that
* can be read synchronously.
*/
virtual void start_consuming() { cli_.start_consuming(); }
/**
* Stop consuming messages.
* This shuts down the internal callback and discards any unread
* messages.
*/
virtual void stop_consuming() { cli_.stop_consuming(); }
/**
* Read the next message from the queue.
* This blocks until a new message arrives.
* @return The message and topic.
*/
virtual const_message_ptr consume_message() { return cli_.consume_message(); }
/**
* Try to read the next message from the queue without blocking.
* @param msg Pointer to the value to receive the message
* @return @em true is a message was read, @em false if no message was
* available.
*/
virtual bool try_consume_message(const_message_ptr* msg) {
return cli_.try_consume_message(msg);
}
/**
* Waits a limited time for a message to arrive.
* @param msg Pointer to the value to receive the message
* @param relTime The maximum amount of time to wait for a message.
* @return @em true if a message was read, @em false if a timeout
* occurred.
*/
template <typename Rep, class Period>
bool try_consume_message_for(
const_message_ptr* msg, const std::chrono::duration<Rep, Period>& relTime
) {
return cli_.try_consume_message_for(msg, relTime);
}
/**
* Waits until a specific time for a message to occur.
* @param msg Pointer to the value to receive the message
* @param absTime The time point to wait until, before timing out.
* @return @em true if a message was read, @em false if a timeout
* occurred.
*/
template <class Clock, class Duration>
bool try_consume_message_until(
const_message_ptr* msg, const std::chrono::time_point<Clock, Duration>& absTime
) {
return cli_.try_consume_message_until(msg, absTime);
}
};
/** Smart/shared pointer to an MQTT synchronous client object */
using client_ptr = client::ptr_t;
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_client_h

View File

@ -0,0 +1,954 @@
/////////////////////////////////////////////////////////////////////////////
/// @file connect_options.h
/// Declaration of MQTT connect_options class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_connect_options_h
#define __mqtt_connect_options_h
#include <chrono>
#include <map>
#include <vector>
#include "MQTTAsync.h"
#include "mqtt/message.h"
#include "mqtt/platform.h"
#include "mqtt/ssl_options.h"
#include "mqtt/string_collection.h"
#include "mqtt/token.h"
#include "mqtt/topic.h"
#include "mqtt/types.h"
#include "mqtt/will_options.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Holds the set of options that control how the client connects to a
* server.
*/
class connect_options
{
/** The default C struct for non-WebSocket connections */
static constexpr MQTTAsync_connectOptions DFLT_C_STRUCT
MQTTAsync_connectOptions_initializer;
/** The default C struct for non-Websocket MQTT v5 connections */
static constexpr MQTTAsync_connectOptions DFLT_C_STRUCT5
MQTTAsync_connectOptions_initializer5;
/** The default C struct for WebSocket connections */
static constexpr MQTTAsync_connectOptions DFLT_C_STRUCT_WS
MQTTAsync_connectOptions_initializer_ws;
/** The default C struct for Websocket MQTT v5 connections */
static constexpr MQTTAsync_connectOptions DFLT_C_STRUCT5_WS
MQTTAsync_connectOptions_initializer5_ws;
/** The underlying C connection options */
MQTTAsync_connectOptions opts_;
/** The LWT options */
will_options will_;
/** The SSL options */
ssl_options ssl_;
/** The user name to use for the connection. */
string_ref userName_;
/** The password to use for the connection. */
binary_ref password_;
/** Shared token pointer for context, if any */
token_ptr tok_;
/** Collection of server URIs, if any */
const_string_collection_ptr serverURIs_;
/** The connect properties */
properties props_;
/** HTTP Headers */
name_value_collection httpHeaders_;
/** HTTP proxy for websockets */
string httpProxy_;
/** Secure HTTPS proxy for websockets */
string httpsProxy_;
/** The client has special access */
friend class async_client;
/**
* Gets a pointer to the C-language NUL-terminated strings for the
* struct.
* @note In the connect options, by default, the Paho C treats
* nullptr char arrays as unset values, so we keep that semantic and
* only set those char arrays if the string is non-empty.
* @param sr The C++ string object.
* @return Pointer to a NUL terminated string. This is only valid until
* the next time the string is updated.
*/
const char* c_str(const string_ref& sr) { return sr.empty() ? nullptr : sr.c_str(); }
const char* c_str(const string& s) { return s.empty() ? nullptr : s.c_str(); }
/**
* Updates the underlying C structure to match our strings.
*/
void update_c_struct();
/**
* Creates the options from a C option struct.
* @param copts The C options struct.
*/
connect_options(const MQTTAsync_connectOptions& copts) : opts_(copts) {}
public:
/** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<connect_options>;
/** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const connect_options>;
/**
* Constructs a new object using the default values.
*
* @param ver The MQTT protocol version.
*/
explicit connect_options(int ver = MQTTVERSION_DEFAULT);
/**
* Constructs a new object using the specified user name and password.
* @param userName The name of the user for connecting to the server
* @param password The password for connecting to the server
* @param ver The MQTT protocol version.
*/
connect_options(string_ref userName, binary_ref password, int ver = MQTTVERSION_DEFAULT);
/**
* Copy constructor.
* @param opt Another object to copy.
*/
connect_options(const connect_options& opt);
/**
* Move constructor.
* @param opt Another object to move into this new one.
*/
connect_options(connect_options&& opt);
/**
* Creates default options for an MQTT v3.x connection.
* @return Default options for an MQTT v3.x connection.
*/
static connect_options v3() { return connect_options(DFLT_C_STRUCT); }
/**
* Creates default options for an MQTT v5 connection.
* @return Default options for an MQTT v5 connection.
*/
static connect_options v5() { return connect_options(DFLT_C_STRUCT5); }
/**
* Creates default options for an MQTT v3.x connection using WebSockets.
*
* The keepalive interval is set to 45 seconds to avoid webserver 60
* second inactivity timeouts.
*
* @return Default options for an MQTT v3.x connection using websockets.
*/
static connect_options ws() { return connect_options(DFLT_C_STRUCT_WS); }
/**
* Creates default options for an MQTT v5 connection using WebSockets.
*
* The keepalive interval is set to 45 seconds to avoid webserver 60
* second inactivity timeouts.
*
* @return Default options for an MQTT v5 connection using websockets.
*/
static connect_options v5_ws() { return connect_options(DFLT_C_STRUCT5_WS); }
/**
* Copy assignment.
* @param opt Another object to copy.
*/
connect_options& operator=(const connect_options& opt);
/**
* Move assignment.
* @param opt Another object to move into this new one.
*/
connect_options& operator=(connect_options&& opt);
/**
* Expose the underlying C struct for the unit tests.
*/
#if defined(UNIT_TESTS)
const MQTTAsync_connectOptions& c_struct() const { return opts_; }
#endif
/**
* Gets the "keep alive" interval.
* @return The keep alive interval in seconds.
*/
std::chrono::seconds get_keep_alive_interval() const {
return std::chrono::seconds(opts_.keepAliveInterval);
}
/**
* Gets the connection timeout.
* This is the amount of time the underlying library will wait for a
* timeout before failing.
* @return The connect timeout in seconds.
*/
std::chrono::seconds get_connect_timeout() const {
return std::chrono::seconds(opts_.connectTimeout);
}
/**
* Gets the user name to use for the connection.
* @return The user name to use for the connection.
*/
string get_user_name() const { return userName_ ? userName_.to_string() : string(); }
/**
* Gets the password to use for the connection.
* @return The password to use for the connection.
*/
binary_ref get_password() const { return password_; }
/**
* Gets the password to use for the connection.
* @return The password to use for the connection.
*/
string get_password_str() const { return password_ ? password_.to_string() : string(); }
/**
* Gets the maximum number of messages that can be in-flight
* simultaneously.
* @return The maximum number of inflight messages.
*/
int get_max_inflight() const { return opts_.maxInflight; }
/**
* Gets the topic to be used for last will and testament (LWT).
* @return The topic to be used for last will and testament (LWT).
*/
string get_will_topic() const { return will_.get_topic(); }
/**
* Gets the message to be sent as last will and testament (LWT).
* @return The message to be sent as last will and testament (LWT).
*/
const_message_ptr get_will_message() const { return will_.get_message(); }
/**
* Get the LWT options to use for the connection.
* @return The LWT options to use for the connection.
*/
const will_options& get_will_options() const { return will_; }
/**
* Get the SSL options to use for the connection.
* @return The SSL options to use for the connection.
*/
const ssl_options& get_ssl_options() const { return ssl_; }
/**
* Sets the SSL for the connection.
* These will only have an effect if compiled against the SSL version of
* the Paho C library, and using a secure connection, "ssl://" or
* "wss://".
* @param ssl The SSL options.
*/
void set_ssl(const ssl_options& ssl);
/**
* Sets the SSL for the connection.
* These will only have an effect if compiled against the SSL version of
* the Paho C library, and using a secure connection, "ssl://" or
* "wss://".
* @param ssl The SSL options.
*/
void set_ssl(ssl_options&& ssl);
/**
* Returns whether the server should remember state for the client
* across reconnects. This only applies to MQTT v3.x connections.
* @return @em true if requesting a clean session, @em false if not.
*/
bool is_clean_session() const { return to_bool(opts_.cleansession); }
/**
* Returns whether the server should remember state for the client
* across reconnects. This only applies to MQTT v5 connections.
* @return @em true if requesting a clean start, @em false if not.
*/
bool is_clean_start() const { return to_bool(opts_.cleanstart); }
/**
* Gets the token used as the callback context.
* @return The delivery token used as the callback context.
*/
token_ptr get_token() const { return tok_; }
/**
* Gets the list of servers to which the client will connect.
* @return A collection of server URI's. Each entry should be of the
* form @em protocol://host:port where @em protocol must be tcp
* or @em ssl. For @em host, you can specify either an IP
* address or a domain name.
*/
const_string_collection_ptr get_servers() const { return serverURIs_; }
/**
* Gets the version of MQTT to be used for the connection.
* @return The version of MQTT to be used for the connection:
* @li MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if that
* fails, fall back to 3.1
* @li MQTTVERSION_3_1 (3) = only try version 3.1
* @li MQTTVERSION_3_1_1 (4) = only try version 3.1.1
*/
int get_mqtt_version() const { return opts_.MQTTVersion; }
/**
* Determines if the options have been configured for automatic
* reconnect.
* @return @em true if configured for automatic reconnect, @em false if
* not.
*/
bool get_automatic_reconnect() const { return to_bool(opts_.automaticReconnect); }
/**
* Gets the minimum retry interval for automatic reconnect.
* @return The minimum retry interval for automatic reconnect, in
* seconds.
*/
std::chrono::seconds get_min_retry_interval() const {
return std::chrono::seconds(opts_.minRetryInterval);
}
/**
* Gets the maximum retry interval for automatic reconnect.
* @return The maximum retry interval for automatic reconnect, in
* seconds.
*/
std::chrono::seconds get_max_retry_interval() const {
return std::chrono::seconds(opts_.maxRetryInterval);
}
/**
* Sets whether the server should remember state for the client across
* reconnects. (MQTT v3.x only)
*
* This will only take effect if the version is _already_ set to v3.x
* (not v5).
*
* @param cleanSession @em true if the server should NOT remember state
* for the client across reconnects, @em false otherwise.
*/
void set_clean_session(bool cleanSession);
/**
* Sets whether the server should remember state for the client across
* reconnects. (MQTT v5 only)
*
* If a persistent session is desired (turning this off), then the app
* should also set the `Session Expiry Interval` property, and add that
* to the connect options.
*
* This will only take effect if the MQTT version is set to v5
*
* @param cleanStart @em true if the server should NOT remember state
* for the client across reconnects, @em false otherwise.
*/
void set_clean_start(bool cleanStart);
/**
* Sets the "keep alive" interval.
* This is the maximum time that should pass without communications
* between client and server. If no messages pass in this time, the
* client will ping the broker.
* @param keepAliveInterval The keep alive interval in seconds.
*/
void set_keep_alive_interval(int keepAliveInterval) {
opts_.keepAliveInterval = keepAliveInterval;
}
/**
* Sets the "keep alive" interval with a chrono duration.
* This is the maximum time that should pass without communications
* between client and server. If no messages pass in this time, the
* client will ping the broker.
* @param interval The keep alive interval.
*/
template <class Rep, class Period>
void set_keep_alive_interval(const std::chrono::duration<Rep, Period>& interval) {
// TODO: Check range
set_keep_alive_interval((int)to_seconds_count(interval));
}
/**
* Sets the connect timeout in seconds.
* This is the maximum time that the underlying library will wait for a
* connection before failing.
* @param timeout The connect timeout in seconds.
*/
void set_connect_timeout(int timeout) { opts_.connectTimeout = timeout; }
/**
* Sets the connect timeout with a chrono duration.
* This is the maximum time that the underlying library will wait for a
* connection before failing.
* @param timeout The connect timeout in seconds.
*/
template <class Rep, class Period>
void set_connect_timeout(const std::chrono::duration<Rep, Period>& timeout) {
// TODO: check range
set_connect_timeout((int)to_seconds_count(timeout));
}
/**
* Sets the user name to use for the connection.
* @param userName The user name for connecting to the MQTT broker.
*/
void set_user_name(string_ref userName);
/**
* Sets the password to use for the connection.
* @param password The password for connecting to the MQTT broker.
*/
void set_password(binary_ref password);
/**
* Sets the maximum number of messages that can be in-flight
* simultaneously.
* @param n The maximum number of inflight messages.
*/
void set_max_inflight(int n) { opts_.maxInflight = n; }
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param will The LWT options.
*/
void set_will(const will_options& will);
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param will The LWT options.
*/
void set_will(will_options&& will);
/**
* Sets the "Last Will and Testament" (LWT) as a message
* @param msg The LWT message
*/
void set_will_message(const message& msg) { set_will(will_options(msg)); }
/**
* Sets the "Last Will and Testament" (LWT) as a message
* @param msg Pointer to a LWT message
*/
void set_will_message(const_message_ptr msg) {
if (msg)
set_will(will_options(*msg));
}
/**
* Sets the callback context to a delivery token.
* @param tok The delivery token to be used as the callback context.
*/
void set_token(const token_ptr& tok);
/**
* Sets the list of servers to which the client will connect.
* @param serverURIs A pointer to a collection of server URI's. Each
* entry should be of the form @em
* protocol://host:port where @em protocol must be
* @em tcp or @em ssl. For @em host, you can specify
* either an IP address or a domain name.
*/
void set_servers(const_string_collection_ptr serverURIs);
/**
* Sets the version of MQTT to be used on the connect.
*
* This will also set other connect options to legal values dependent on
* the selected version.
*
* @param mqttVersion The MQTT version to use for the connection:
* @li MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if
* that fails, fall back to 3.1
* @li MQTTVERSION_3_1 (3) = only try version 3.1
* @li MQTTVERSION_3_1_1 (4) = only try version 3.1.1
* @li MQTTVERSION_5 (5) = only try version 5
*
* Note that it is preferable to create the options for the desired
* version rather than using this function to change the version after
* some parameters have already been set. If you do use this function,
* call it before setting any other version-specific options. @sa
* connect_options::v5()
*/
void set_mqtt_version(int mqttVersion);
/**
* Enable or disable automatic reconnects.
* The retry intervals are not affected.
* @param on Whether to turn reconnects on or off
*/
void set_automatic_reconnect(bool on) { opts_.automaticReconnect = to_int(on); }
/**
* Enable or disable automatic reconnects.
* @param minRetryInterval Minimum retry interval in seconds. Doubled
* on each failed retry.
* @param maxRetryInterval Maximum retry interval in seconds. The
* doubling stops here on failed retries.
*/
void set_automatic_reconnect(int minRetryInterval, int maxRetryInterval);
/**
* Enable or disable automatic reconnects.
* @param minRetryInterval Minimum retry interval. Doubled on each
* failed retry.
* @param maxRetryInterval Maximum retry interval. The doubling stops
* here on failed retries.
*/
template <class Rep1, class Period1, class Rep2, class Period2>
void set_automatic_reconnect(
const std::chrono::duration<Rep1, Period1>& minRetryInterval,
const std::chrono::duration<Rep2, Period2>& maxRetryInterval
) {
set_automatic_reconnect(
(int)to_seconds_count(minRetryInterval), (int)to_seconds_count(maxRetryInterval)
);
}
/**
* Gets the connect properties.
* @return A const reference to the properties for the connect.
*/
const properties& get_properties() const { return props_; }
/**
* Gets a mutable reference to the connect properties.
* @return A reference to the properties for the connect.
*/
properties& get_properties() { return props_; }
/**
* Sets the properties for the connect.
* @param props The properties to place into the message.
*/
void set_properties(const properties& props);
/**
* Moves the properties for the connect.
* @param props The properties to move into the connect object.
*/
void set_properties(properties&& props);
/**
* Gets the HTTP headers
* @return A const reference to the HTTP headers name/value collection.
*/
const name_value_collection& get_http_headers() const { return httpHeaders_; }
/**
* Sets the HTTP headers for the connection.
* @param httpHeaders The header nam/value collection.
*/
void set_http_headers(const name_value_collection& httpHeaders) {
httpHeaders_ = httpHeaders;
opts_.httpHeaders = httpHeaders_.empty() ? nullptr : httpHeaders_.c_arr();
}
/**
* Sets the HTTP headers for the connection.
* @param httpHeaders The header nam/value collection.
*/
void set_http_headers(name_value_collection&& httpHeaders) {
httpHeaders_ = std::move(httpHeaders);
opts_.httpHeaders = httpHeaders_.empty() ? nullptr : httpHeaders_.c_arr();
}
/**
* Gets the HTTP proxy setting.
* @return The HTTP proxy setting. An empty string means no proxy.
*/
string get_http_proxy() const { return httpProxy_; }
/**
* Sets the HTTP proxy setting.
* @param httpProxy The HTTP proxy setting. An empty string means no
* proxy.
*/
void set_http_proxy(const string& httpProxy);
/**
* Gets the secure HTTPS proxy setting.
* @return The HTTPS proxy setting. An empty string means no proxy.
*/
string get_https_proxy() const { return httpsProxy_; }
/**
* Sets the secure HTTPS proxy setting.
* @param httpsProxy The HTTPS proxy setting. An empty string means no
* proxy.
*/
void set_https_proxy(const string& httpsProxy);
};
/** Smart/shared pointer to a connection options object. */
using connect_options_ptr = connect_options::ptr_t;
/////////////////////////////////////////////////////////////////////////////
/**
* The connect options that can be updated before an automatic reconnect.
*/
class connect_data
{
/** The default C struct */
PAHO_MQTTPP_EXPORT static constexpr MQTTAsync_connectData DFLT_C_STRUCT
MQTTAsync_connectData_initializer;
/** The underlying C connect data */
MQTTAsync_connectData data_{DFLT_C_STRUCT};
/** The user name to use for the connection. */
string_ref userName_;
/** The password to use for the connection. (Optional) */
binary_ref password_;
/** The client has special access */
friend class async_client;
/**
* Updates the underlying C structure to match our strings.
*/
void update_c_struct();
/**
* Create data from a C struct
* This is a deep copy of the data from the C struct.
* @param cdata The C connect data.
*/
connect_data(const MQTTAsync_connectData& cdata);
public:
/**
* Creates an empty set of connection data.
*/
connect_data();
/**
* Creates connection data with a user name, but no password.
* @param userName The user name for reconnecting to the MQTT broker.
*/
explicit connect_data(string_ref userName);
/**
* Creates connection data with a user name and password.
* @param userName The user name for reconnecting to the MQTT broker.
* @param password The password for connecting to the MQTT broker.
*/
connect_data(string_ref userName, binary_ref password);
/**
* Copy constructor
* @param other Another data struct to copy into this one.
*/
connect_data(const connect_data& other);
/**
* Copy the connection data.
* @param rhs Another data struct to copy into this one.
* @return A reference to this data
*/
connect_data& operator=(const connect_data& rhs);
/**
* Gets the user name to use for the connection.
* @return The user name to use for the connection.
*/
string get_user_name() const { return userName_ ? userName_.to_string() : string(); }
/**
* Gets the password to use for the connection.
* @return The password to use for the connection.
*/
binary_ref get_password() const { return password_; }
/**
* Sets the user name to use for the connection.
* @param userName The user name for connecting to the MQTT broker.
*/
void set_user_name(string_ref userName);
/**
* Sets the password to use for the connection.
* @param password The password for connecting to the MQTT broker.
*/
void set_password(binary_ref password);
};
/////////////////////////////////////////////////////////////////////////////
/**
* Class to build connect options.
*/
class connect_options_builder
{
connect_options opts_;
public:
/** This class */
using self = connect_options_builder;
/**
* Default constructor.
*
* @param ver The MQTT version for the connection. Defaults to the most
* recent v3 supported by the server.
*/
explicit connect_options_builder(int ver = MQTTVERSION_DEFAULT) : opts_(ver) {}
/**
* Copy constructor from an existing set of options.
*/
explicit connect_options_builder(const connect_options& opts) : opts_(opts) {}
/**
* Move constructor from an existing set of options.
*/
explicit connect_options_builder(const connect_options&& opts) : opts_(std::move(opts)) {}
/**
* Creates the default options builder for an MQTT v3.x connection.
* @return An options builder for an MQTT v3.x connection.
*/
static connect_options_builder v3() {
return connect_options_builder{connect_options::v3()};
}
/**
* Creates the default options builder for an MQTT v5 connection.
* @return An options builder for an MQTT v5 connection.
*/
static connect_options_builder v5() {
return connect_options_builder{connect_options::v5()};
}
/**
* Creates the default options builder for an MQTT v3.x connection using
* WebSockets.
*
* The keepalive interval is set to 45 seconds to avoid webserver 60
* second inactivity timeouts.
*
* @return An options builder for an MQTT v3.x connection using
* websockets.
*/
static connect_options_builder ws() {
return connect_options_builder{connect_options::ws()};
}
/**
* Creates the default options for an MQTT v5 connection using
* WebSockets
* .
* The keepalive interval is set to 45 seconds to avoid webserver 60
* second inactivity timeouts.
*
* @return An options builder for an MQTT v5 connection using
* websockets.
*/
static connect_options_builder v5_ws() {
return connect_options_builder{connect_options::v5_ws()};
}
/**
* Sets whether the server should remember state for the client across
* reconnects. (MQTT v3.x only)
* @param on @em true if the server should NOT remember state for the
* client across reconnects, @em false otherwise.
*/
auto clean_session(bool on = true) -> self& {
opts_.set_clean_session(on);
return *this;
}
/**
* Sets the "keep alive" interval with a chrono duration.
* This is the maximum time that should pass without communications
* between client and server. If no messages pass in this time, the
* client will ping the broker.
* @param interval The keep alive interval.
*/
template <class Rep, class Period>
auto keep_alive_interval(const std::chrono::duration<Rep, Period>& interval) -> self& {
opts_.set_keep_alive_interval(interval);
return *this;
}
/**
* Sets the connect timeout with a chrono duration.
* This is the maximum time that the underlying library will wait for a
* connection before failing.
* @param timeout The connect timeout in seconds.
*/
template <class Rep, class Period>
auto connect_timeout(const std::chrono::duration<Rep, Period>& timeout) -> self& {
opts_.set_connect_timeout(timeout);
return *this;
}
/**
* Sets the user name for the connection.
* @param userName The user name for the connection.
*/
auto user_name(string_ref userName) -> self& {
opts_.set_user_name(userName);
return *this;
}
/**
* Sets the password for the connection.
* @param password The password for the connection.
*/
auto password(binary_ref password) -> self& {
opts_.set_password(password);
return *this;
}
/**
* Sets the maximum number of messages that can be in-flight
* simultaneously.
* @param n The maximum number of inflight messages.
*/
auto max_inflight(int n) -> self& {
opts_.set_max_inflight(n);
return *this;
}
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param will The LWT options.
*/
auto will(const will_options& will) -> self& {
opts_.set_will(will);
return *this;
}
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param will The LWT options.
*/
auto will(will_options&& will) -> self& {
opts_.set_will(std::move(will));
return *this;
}
/**
* Sets the "Last Will and Testament" (LWT) as a message
* @param msg The LWT message
*/
auto will(const message& msg) -> self& {
opts_.set_will_message(msg);
return *this;
}
/**
* Sets the SSL options for the connection.
* These will only have an effect if compiled against the SSL version of
* the Paho C library, and connecting with a secure URI.
* @param ssl The SSL options.
*/
auto ssl(const ssl_options& ssl) -> self& {
opts_.set_ssl(ssl);
return *this;
}
/**
* Sets the SSL options for the connection.
* These will only have an effect if compiled against the SSL version of
* the Paho C library, and connecting with a secure URI.
* @param ssl The SSL options.
*/
auto ssl(ssl_options&& ssl) -> self& {
opts_.set_ssl(std::move(ssl));
return *this;
}
/**
* Sets the callback context to a delivery token.
* @param tok The delivery token to be used as the callback context.
*/
auto token(const token_ptr& tok) -> self& {
opts_.set_token(tok);
return *this;
}
/**
* Sets the list of servers to which the client will connect.
* @param serverURIs A pointer to a collection of server URI's. Each
* entry should be of the form @em
* protocol://host:port where @em protocol must be
* @em tcp or @em ssl. For @em host, you can specify
* either an IP address or a domain name.
*/
auto servers(const_string_collection_ptr serverURIs) -> self& {
opts_.set_servers(serverURIs);
return *this;
}
/**
* Sets the version of MQTT to be used on the connect.
*
* This will also set other connect options to legal values dependent on
* the selected version.
*
* @param ver The MQTT protocol version to use for the connection:
* @li MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if
* that fails, fall back to 3.1
* @li MQTTVERSION_3_1 (3) = only try version 3.1
* @li MQTTVERSION_3_1_1 (4) = only try version 3.1.1
* @li MQTTVERSION_5 (5) = only try version 5
*
* Note that it is preferable to create the options builder for the
* desired version rather than using this function to change the version
* after some parameters have already been set. If you do use this
* function, call it before setting any other version-specific options.
* @sa connect_options_builder::v5()
*/
auto mqtt_version(int ver) -> self& {
opts_.set_mqtt_version(ver);
return *this;
}
/**
* Enable or disable automatic reconnects.
* The retry intervals are not affected.
* @param on Whether to turn reconnects on or off
*/
auto automatic_reconnect(bool on = true) -> self& {
opts_.set_automatic_reconnect(on);
return *this;
}
/**
* Enable or disable automatic reconnects.
* @param minRetryInterval Minimum retry interval. Doubled on each
* failed retry.
* @param maxRetryInterval Maximum retry interval. The doubling stops
* here on failed retries.
*/
template <class Rep1, class Period1, class Rep2, class Period2>
auto automatic_reconnect(
const std::chrono::duration<Rep1, Period1>& minRetryInterval,
const std::chrono::duration<Rep2, Period2>& maxRetryInterval
) -> self& {
opts_.set_automatic_reconnect(minRetryInterval, maxRetryInterval);
return *this;
}
/**
* Sets the 'clean start' flag for the connection. (MQTT v5 only)
* @param on @em true to set the 'clean start' flag for the connect,
* @em false otherwise.
*/
auto clean_start(bool on = true) -> self& {
opts_.set_clean_start(on);
return *this;
}
/**
* Sets the properties for the connect message.
* @param props The properties for the connect message.
*/
auto properties(const mqtt::properties& props) -> self& {
opts_.set_properties(props);
return *this;
}
/**
* Sets the properties for the connect message.
* @param props The properties for the connect message.
*/
auto properties(mqtt::properties&& props) -> self& {
opts_.set_properties(std::move(props));
return *this;
}
/**
* Sets the HTTP headers for the connection.
* @param headers The header nam/value collection.
*/
auto http_headers(const name_value_collection& headers) -> self& {
opts_.set_http_headers(headers);
return *this;
}
/**
* Sets the HTTP headers for the connection.
* @param headers The header nam/value collection.
*/
auto http_headers(name_value_collection&& headers) -> self& {
opts_.set_http_headers(std::move(headers));
return *this;
}
/**
* Sets the HTTP proxy setting.
* @param httpProxy The HTTP proxy setting. An empty string means no
* proxy.
*/
auto http_proxy(const string& httpProxy) -> self& {
opts_.set_http_proxy(httpProxy);
return *this;
}
/**
* Sets the secure HTTPS proxy setting.
* @param httpsProxy The HTTPS proxy setting. An empty string means no
* proxy.
*/
auto https_proxy(const string& httpsProxy) -> self& {
opts_.set_https_proxy(httpsProxy);
return *this;
}
/**
* Finish building the options and return them.
* @return The option struct as built.
*/
connect_options finalize() { return opts_; }
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_connect_options_h

View File

@ -0,0 +1,430 @@
/////////////////////////////////////////////////////////////////////////////
/// @file create_options.h
/// Declaration of MQTT create_options class
/// @date Oct 17, 2020
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2020-2025 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_create_options_h
#define __mqtt_create_options_h
#include <variant>
#include "MQTTAsync.h"
#include "mqtt/iclient_persistence.h"
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/** An empty type that can be used as a `persistent_type` variant option. */
struct no_persistence
{
};
/** A constant used to indicate that no persistence is desired */
constexpr no_persistence NO_PERSISTENCE{};
/**
* A variant for the different type of persistence:
* @li @em no_persistence: Any object of this type indicates no persistence
* is desired.
* @li @em string: Indicates file persistence. The string specifies the
* directory for the persistence store.
* @li @em iclient_persistence*: User-defined persistence
*/
using persistence_type = std::variant<no_persistence, string, iclient_persistence*>;
/////////////////////////////////////////////////////////////////////////////
/**
* The set of options for constructing a client object.
*
* Note that the numerous, incomplete set of constructors pre-date the
* current, expanded, options structure. For a full set of create options, a
* builder can be used to specify the options, then construct the client
* with those options, like this:
*
* @code
* auto createOpts = mqtt::create_options_builder()
* .server_uri(serverURI)
* .send_while_disconnected()
* .max_buffered_messages(25)
* .delete_oldest_messages()
* .finalize();
*
* mqtt::async_client cli(createOpts);
* @endcode
*/
class create_options
{
/** The underlying C options */
MQTTAsync_createOptions opts_ MQTTAsync_createOptions_initializer5;
/** The address of the server to connect to, specified as a URI */
string serverURI_{};
/** A client identifier that is unique on the server */
string clientId_{};
/** The persistence for the client */
persistence_type persistence_{};
/** The client and tests have special access */
friend class async_client;
friend class create_options_builder;
public:
/** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<create_options>;
/** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const create_options>;
/**
* Default set of client create options.
*/
create_options() {}
/**
* Default create options for the specified version of MQTT.
* @param mqttVersion The MQTT version used to create the client.
*/
explicit create_options(int mqttVersion) : create_options() {
opts_.MQTTVersion = mqttVersion;
}
/**
* Default create options, but with off-line buffering enabled.
* @param mqttVersion The MQTT version used to create the client.
* @param maxBufferedMessages the maximum number of messages allowed to
* be buffered while not connected
*/
create_options(int mqttVersion, int maxBufferedMessages);
/**
* Create options for the specified server and client ID, with optional
* persistence.
* This allows the caller to specify a user-defined persistence object,
* or use no persistence.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param persistence The desired persistence structure.
* @throw exception if an argument is invalid
*/
explicit create_options(
const string& serverURI, const string& clientId = string{},
const persistence_type& persistence = NO_PERSISTENCE
)
: serverURI_{serverURI}, clientId_{clientId}, persistence_{persistence} {}
/**
* Create an async_client that can be used to communicate with an MQTT
* server, which allows for off-line message buffering.
* This uses file-based persistence in the specified directory.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param maxBufferedMessages the maximum number of messages allowed to
* be buffered while not connected
* @param persistence The persistence that the client should use.
* @throw exception if an argument is invalid
*/
create_options(
const string& serverURI, const string& clientId, int maxBufferedMessages,
const persistence_type& persistence = NO_PERSISTENCE
)
: serverURI_{serverURI}, clientId_{clientId}, persistence_{persistence} {
opts_.maxBufferedMessages = maxBufferedMessages;
}
/**
* Create an async_client that can be used to communicate with an MQTT
* server, which allows for off-line message buffering.
* This uses file-based persistence in the specified directory.
* @param serverURI the address of the server to connect to, specified
* as a URI.
* @param clientId a client identifier that is unique on the server
* being connected to
* @param opts The create options
* @param persistence The persistence that the client should use.
* @throw exception if an argument is invalid
*/
create_options(
const string& serverURI, const string& clientId, const create_options& opts,
const persistence_type& persistence
)
: opts_{opts.opts_},
serverURI_{serverURI},
clientId_{clientId},
persistence_{persistence} {}
/**
* Copy constructor.
* @param opts The other options.
*/
create_options(const create_options& opts)
: opts_{opts.opts_},
serverURI_{opts.serverURI_},
clientId_{opts.clientId_},
persistence_{opts.persistence_} {}
/**
* Move constructor.
* @param opts The other options.
*/
create_options(create_options&& opts)
: opts_{opts.opts_},
serverURI_{std::move(opts.serverURI_)},
clientId_{std::move(opts.clientId_)},
persistence_{std::move(opts.persistence_)} {}
create_options& operator=(const create_options& rhs);
create_options& operator=(create_options&& rhs);
/**
* Set the address of the server to connect to, specified as a URI
* @param serverURI The URI of the server.
*/
void set_server_uri(const string& serverURI) { serverURI_ = serverURI; };
/**
* Get the address of the server to connect to, specified as a URI.
* @return The URI of the server.
*/
const string& get_server_uri() const noexcept { return serverURI_; };
/**
* Set the client identifier.
* @param clientId The client identifier.
*/
void set_client_id(const string& clientId) { clientId_ = clientId; }
/**
* Get the client identifier.
* @return The client identifier.
*/
const string& get_client_id() const noexcept { return clientId_; }
/**
* Set the persistence for the client.
* @param persistence The persistence for the client
*/
void set_persistence(const persistence_type& persistence) { persistence_ = persistence; }
/**
* Get the persistence for the client.
* @return The persistence for the client
*/
const persistence_type& get_persistence() const noexcept { return persistence_; }
/**
* Gets whether the client will accept message to publish while
* disconnected.
*/
bool get_send_while_disconnected() const { return to_bool(opts_.sendWhileDisconnected); }
/**
* Sets whether the client will accept message to publish while
* disconnected.
*
* @param on @em true to allow the application to publish messages while
* disconnected, @em false returns an error on publish if
* disconnected.
* @param anyTime If @em true, allows you to publish messages before the
* first successful connection.
*/
void set_send_while_disconnected(bool on, bool anyTime = false) {
opts_.sendWhileDisconnected = to_int(on);
opts_.allowDisconnectedSendAtAnyTime = to_int(anyTime);
}
/**
* Gets the maximum number of offline buffered messages.
* @return The maximum number of offline buffered messages.
*/
int get_max_buffered_messages() const { return opts_.maxBufferedMessages; }
/**
* Sets the maximum number of offline buffered messages.
* @param n The maximum number of offline buffered messages.
*/
void set_max_buffered_messages(int n) { opts_.maxBufferedMessages = n; }
/**
* Gets the MQTT version used to create the client.
* @return The MQTT version used to create the client.
*/
int mqtt_version() const { return opts_.MQTTVersion; }
/**
* Sets the MQTT version used to create the client.
* @param ver The MQTT version used to create the client.
*/
void set_mqtt_version(int ver) { opts_.MQTTVersion = ver; }
/**
* Whether the oldest messages are deleted when the output buffer is
* full.
*
* @return @em true if the oldest messages should be deleted when the
* output buffer is full, @em false if the new messages should
* be dropped when the buffer is full.
*/
bool get_delete_oldest_messages() const { return to_bool(opts_.deleteOldestMessages); }
/**
* Determines what to do when the maximum number of buffered messages is
* reached: delete the oldest messages rather than the newest
* @param on @em true When the output queue is full, delete the oldest
* message, @em false drop the newest message being added.
*/
void set_delete_oldest_messages(bool on) { opts_.deleteOldestMessages = to_int(on); }
/**
* Whether the messages will be restored from persistence or the store
* will be cleared.
* @return @em true if the messages will be restored from persistence,
* @em false if the persistence store will be cleared.
*/
bool get_restore_messages() const { return to_bool(opts_.restoreMessages); }
/**
* Determine whether to restore messages from persistence or clear the
* persistence store.
* @param on @em true to restore messages from persistence, @em false to
* clear the persistence store.
*/
void set_restore_messages(bool on) { opts_.restoreMessages = to_int(on); }
/**
* Whether to persist QoS 0 messages.
*
* @return @em true if QoS 0 messages are persisted, @em false if not.
*/
bool get_persist_qos0() const { return to_bool(opts_.persistQoS0); }
/**
* Determine whether to persist QoS 0 messages.
*
* @param on @em true if QoS 0 messages are persisted, @em false if not.
*/
void set_persist_qos0(bool on) { opts_.persistQoS0 = to_int(on); }
};
/** Smart/shared pointer to a connection options object. */
using create_options_ptr = create_options::ptr_t;
/////////////////////////////////////////////////////////////////////////////
/**
* Builder class to generate the create options.
*/
class create_options_builder
{
/** The underlying options */
create_options opts_;
public:
/** This class */
using self = create_options_builder;
/**
* Default constructor.
*/
create_options_builder() {}
/**
* Set the server URI.
* @param serverURI The address of the server to connect to, specified
* as a URI
*/
auto server_uri(const string& serverURI) -> self& {
opts_.set_server_uri(serverURI);
return *this;
}
/**
* Sets the client ID.
* @param clientId A client identifier that is unique on the server
*/
auto client_id(const string& clientId) -> self& {
opts_.set_client_id(clientId);
return *this;
}
/**
* Sets the persistence.
* @param persistence The persistence the client should use.
*/
auto persistence(const persistence_type& persistence) -> self& {
opts_.set_persistence(persistence);
return *this;
}
/**
* Sets whether the client will accept message to publish while
* disconnected.
*
* @param on @em true to allow the application to publish messages while
* disconnected, @em false returns an error on publish if
* disconnected.
* @param anyTime If @em true, allows you to publish messages before the
* first successful connection.
* @return A reference to this object.
*/
auto send_while_disconnected(bool on = true, bool anyTime = false) -> self& {
opts_.opts_.sendWhileDisconnected = to_int(on);
opts_.opts_.allowDisconnectedSendAtAnyTime = to_int(anyTime);
return *this;
}
/**
* Sets the maximum number of offline buffered messages.
* @param n The maximum number of offline buffered messages.
* @return A reference to this object.
*/
auto max_buffered_messages(int n) -> self& {
opts_.opts_.maxBufferedMessages = n;
return *this;
}
/**
* Sets the MQTT version used to create the client.
* @param ver The MQTT version used to create the client.
*/
auto mqtt_version(int ver) -> self& {
opts_.opts_.MQTTVersion = ver;
return *this;
}
/**
* Determines what to do when the maximum number of buffered messages is
* reached: delete the oldest messages rather than the newest.
* @param on @em true When the output queue is full, delete the oldest
* message, @em false drop the newest message being added.
* @return A reference to this object.
*/
auto delete_oldest_messages(bool on = true) -> self& {
opts_.opts_.deleteOldestMessages = to_int(on);
return *this;
}
/**
* Determines whether to restore persisted messages or clear the
* persistence store. (Defaults true)
*
* @param on @em true to restore persisted messages, @em false to clear
* the persistence store.
* @return A reference to this object.
*/
auto restore_messages(bool on = true) -> self& {
opts_.opts_.restoreMessages = to_int(on);
return *this;
}
/**
* Whether to persist QoS 0 messages. (Defaults true)
*
* @param on @em true persist QoS 0 messages, @em false, don't.
* @return A reference to this object
*/
auto persist_qos0(bool on = true) -> self& {
opts_.opts_.persistQoS0 = to_int(on);
return *this;
}
/**
* Finish building the options and return them.
* @return The option struct as built.
*/
create_options finalize() { return opts_; }
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_create_options_h

View File

@ -0,0 +1,135 @@
/////////////////////////////////////////////////////////////////////////////
/// @file delivery_token.h
/// Declaration of MQTT delivery_token class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_delivery_token_h
#define __mqtt_delivery_token_h
#include <memory>
#include "MQTTAsync.h"
#include "mqtt/message.h"
#include "mqtt/token.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Provides a mechanism to track the delivery progress of a message.
* Used to track the the delivery progress of a message when a publish is
* executed in a non-blocking manner (run in the background) action.
*/
class delivery_token : public token
{
/** The message being tracked. */
const_message_ptr msg_;
/** Client has special access. */
friend class async_client;
/**
* Sets the message to which this token corresponds.
* @param msg The message to which this token corresponds.
*/
void set_message(const_message_ptr msg) { msg_ = msg; }
public:
/** Smart/shared pointer to an object of this class */
using ptr_t = std::shared_ptr<delivery_token>;
/** Smart/shared pointer to a const object of this class */
using const_ptr_t = std::shared_ptr<delivery_token>;
/** Weak pointer to an object of this class */
using weak_ptr_t = std::weak_ptr<delivery_token>;
/**
* Creates an empty delivery token connected to a particular client.
* @param cli The asynchronous client object.
*/
delivery_token(iasync_client& cli) : token(token::Type::PUBLISH, cli) {}
/**
* Creates a delivery token connected to a particular client.
* @param cli The asynchronous client object.
* @param msg The message being tracked.
*/
delivery_token(iasync_client& cli, const_message_ptr msg)
: token(token::Type::PUBLISH, cli, msg->get_topic()), msg_(std::move(msg)) {}
/**
* Creates a delivery token connected to a particular client.
* @param cli The asynchronous client object.
* @param msg The message data.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback optional listener that will be notified when message
* delivery has completed to the requested quality of
* service
*/
delivery_token(
iasync_client& cli, const_message_ptr msg, void* userContext, iaction_listener& cb
)
: token(token::Type::PUBLISH, cli, msg->get_topic(), userContext, cb),
msg_(std::move(msg)) {}
/**
* Creates an empty delivery token connected to a particular client.
* @param cli The asynchronous client object.
*/
static ptr_t create(iasync_client& cli) { return std::make_shared<delivery_token>(cli); }
/**
* Creates a delivery token connected to a particular client.
* @param cli The asynchronous client object.
* @param msg The message data.
*/
static ptr_t create(iasync_client& cli, const_message_ptr msg) {
return std::make_shared<delivery_token>(cli, msg);
}
/**
* Creates a delivery token connected to a particular client.
* @param cli The asynchronous client object.
* @param msg The message data.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback optional listener that will be notified when message
* delivery has completed to the requested quality of
* service
*/
static ptr_t create(
iasync_client& cli, const_message_ptr msg, void* userContext, iaction_listener& cb
) {
return std::make_shared<delivery_token>(cli, msg, userContext, cb);
}
/**
* Gets the message associated with this token.
* @return The message associated with this token.
*/
virtual const_message_ptr get_message() const { return msg_; }
};
/** Smart/shared pointer to a delivery_token */
using delivery_token_ptr = delivery_token::ptr_t;
/** Smart/shared pointer to a const delivery_token */
using const_delivery_token_ptr = delivery_token::const_ptr_t;
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_delivery_token_h

View File

@ -0,0 +1,278 @@
/////////////////////////////////////////////////////////////////////////////
/// @file disconnect_options.h
/// Implementation of the class 'disconnect_options'
/// @date 26-Aug-2016
/////////////////////////////////////////////////////////////////////////////
/****************************************************************************
* Copyright (c) 2016-2017 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
***************************************************************************/
#ifndef __mqtt_disconnect_options_h
#define __mqtt_disconnect_options_h
#include <chrono>
#include "MQTTAsync.h"
#include "mqtt/properties.h"
#include "mqtt/token.h"
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Options for disconnecting from an MQTT broker.
*/
class disconnect_options
{
/** The default C struct */
static constexpr MQTTAsync_disconnectOptions DFLT_C_STRUCT
MQTTAsync_disconnectOptions_initializer;
/** The default C struct */
static constexpr MQTTAsync_disconnectOptions DFLT_C_STRUCT5
MQTTAsync_disconnectOptions_initializer5;
/** The underlying C disconnect options */
MQTTAsync_disconnectOptions opts_{DFLT_C_STRUCT};
/** Shared token pointer for context, if any */
token_ptr tok_;
/** Disconnect message properties */
properties props_;
/** The client has special access */
friend class async_client;
/** The options builder has special access */
friend class disconnect_options_builder;
/**
* Updates the underlying C structure to match our cached data.
*/
void update_c_struct();
/** Construct options from a C struct */
disconnect_options(const MQTTAsync_disconnectOptions& copts) : opts_{copts} {}
public:
/**
* Create an empty delivery response object.
*/
disconnect_options() {}
/**
* Creates disconnect options tied to the specific token.
* @param timeout The timeout (in milliseconds).
*/
disconnect_options(int timeout) : disconnect_options() { set_timeout(timeout); }
/**
* Creates disconnect options tied to the specific token.
* @param to The timeout.
*/
template <class Rep, class Period>
disconnect_options(const std::chrono::duration<Rep, Period>& to) : disconnect_options() {
set_timeout(to);
}
/**
* Copy constructor.
* @param opt Another object to copy.
*/
disconnect_options(const disconnect_options& opt);
/**
* Move constructor.
* @param opt Another object to move into this new one.
*/
disconnect_options(disconnect_options&& opt);
/**
* Creates default options for an MQTT v3.x connection.
* @return Default options for an MQTT v3.x connection.
*/
static disconnect_options v3() { return disconnect_options{DFLT_C_STRUCT}; }
/**
* Creates default options for an MQTT v5 connection.
* @return Default options for an MQTT v5 connection.
*/
static disconnect_options v5() { return disconnect_options{DFLT_C_STRUCT5}; }
/**
* Copy assignment.
* @param opt Another object to copy.
*/
disconnect_options& operator=(const disconnect_options& opt);
/**
* Move assignment.
* @param opt Another object to move into this new one.
*/
disconnect_options& operator=(disconnect_options&& opt);
/**
* Expose the underlying C struct for the unit tests.
*/
#if defined(UNIT_TESTS)
const MQTTAsync_disconnectOptions& c_struct() const { return opts_; }
#endif
/**
* Gets the timeout used for disconnecting.
* @return The timeout for disconnecting (in milliseconds).
*/
std::chrono::milliseconds get_timeout() const {
return std::chrono::milliseconds(opts_.timeout);
}
/**
* Sets the disconnect timeout, in milliseconds.
* This allows for any remaining in-flight messages to be delivered.
* @param timeout The disconnect timeout (in milliseconds).
*/
void set_timeout(int timeout) { opts_.timeout = timeout; }
/**
* Sets the disconnect timeout with a duration.
* This allows for any remaining in-flight messages to be delivered.
* @param to The disconnect connect timeout.
*/
template <class Rep, class Period>
void set_timeout(const std::chrono::duration<Rep, Period>& to) {
// TODO: check range
set_timeout((int)to_milliseconds_count(to));
}
/**
* Sets the callback context to a delivery token.
* @param tok The delivery token to be used as the callback context.
* @param mqttVersion The version of MQTT we're using for the
* connection.
*/
void set_token(const token_ptr& tok, int mqttVersion);
/**
* Gets the callback context to a delivery token.
* @return The delivery token to be used as the callback context.
*/
token_ptr get_token() const { return tok_; }
/**
* Gets the disconnect properties.
* @return A const reference to the properties for the disconnect.
*/
const properties& get_properties() const { return props_; }
/**
* Gets a mutable reference to the disconnect properties.
* @return A mutable reference to the properties for the disconnect.
*/
properties& get_properties() { return props_; }
/**
* Sets the properties for the connect.
* @param props The properties to place into the message.
*/
void set_properties(const properties& props) {
props_ = props;
opts_.properties = props_.c_struct();
}
/**
* Moves the properties for the connect.
* @param props The properties to move into the connect object.
*/
void set_properties(properties&& props) {
props_ = std::move(props);
opts_.properties = props_.c_struct();
}
/**
* Gets the reason code for the disconnect.
* @return The reason code for the disconnect.
*/
ReasonCode get_reason_code() const { return ReasonCode(opts_.reasonCode); }
/**
* Sets the reason code for the disconnect.
* @param code The reason code for the disconnect.
*/
void set_reason_code(ReasonCode code) { opts_.reasonCode = MQTTReasonCodes(code); }
};
/////////////////////////////////////////////////////////////////////////////
/**
* Class to build connect options.
*/
class disconnect_options_builder
{
/** The underlying options */
disconnect_options opts_;
/** Construct options builder from a C struct */
disconnect_options_builder(const MQTTAsync_disconnectOptions& copts) : opts_{copts} {}
public:
/** This class */
using self = disconnect_options_builder;
/**
* Default constructor.
*/
disconnect_options_builder() {}
/**
* Creates default options builder for an MQTT v3.x connection.
* @return Default options builder for an MQTT v3.x connection.
*/
static disconnect_options_builder v3() {
return disconnect_options_builder{disconnect_options::DFLT_C_STRUCT};
}
/**
* Creates default options builder for an MQTT v5 connection.
* @return Default options builder for an MQTT v5 connection.
*/
static disconnect_options_builder v5() {
return disconnect_options_builder{disconnect_options::DFLT_C_STRUCT5};
}
/**
* Sets the properties for the disconnect message.
* @param props The properties for the disconnect message.
*/
auto properties(mqtt::properties&& props) -> self& {
opts_.set_properties(std::move(props));
return *this;
}
/**
* Sets the properties for the disconnect message.
* @param props The properties for the disconnect message.
*/
auto properties(const mqtt::properties& props) -> self& {
opts_.set_properties(props);
return *this;
}
/**
* Sets the disconnect connect timeout.
* This allows for any remaining in-flight messages to be delivered.
* @param to The disconnect timeout.
*/
template <class Rep, class Period>
auto timeout(const std::chrono::duration<Rep, Period>& to) -> self& {
opts_.set_timeout(to);
return *this;
}
/**
* Sets the reason code for the disconnect.
* @param code The reason code for the disconnect.
*/
auto reason_code(ReasonCode code) -> self& {
opts_.set_reason_code(code);
return *this;
}
/**
* Finish building the options and return them.
* @return The option struct as built.
*/
disconnect_options finalize() { return opts_; }
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_disconnect_options_h

261
include/mqtt/event.h Normal file
View File

@ -0,0 +1,261 @@
/////////////////////////////////////////////////////////////////////////////
/// @file event.h
/// Declaration of MQTT event-related classes
/// @date July 6, 2024
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_event_h
#define __mqtt_event_h
#include <variant>
#include "mqtt/message.h"
#include "mqtt/properties.h"
#include "mqtt/reason_code.h"
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/** Event for when the client is connected/reconnected */
struct connected_event
{
string cause;
};
/** Event for when the connection is lost */
struct connection_lost_event
{
string cause;
};
/** Event for when we receive a DISCONNECT packet from the server */
struct disconnected_event
{
properties props;
ReasonCode reasonCode;
};
/** Event for when the consumer queue is shutdown from another thread */
struct shutdown_event
{
};
/* Event for when a message arrives is just a message pointer */
/////////////////////////////////////////////////////////////////////////////
/**
* An MQTT event.
*
* This is used by the client consumer to pass events and state changes from
* the client to the application without the need for any additional
* callbacks or client queries.
*
* Each instance carries the relevant data for specific event that caused
* it. For example an incoming message event contains a shared pointer to
* the message that arrived.
*
* The supported event types are:
* @li **message** A message arrived from the server.
* @li **connected** The client connected. If the client was configured for
* automatic reconnects, this can be from a reconnection. (No data)
* @li **connection lost** The client lost the connection. (No data)
* @li **disconnected** (v5) The client received a DISCONNECT packet from
* the server. This includes the reason code and properties for the
* disconnect.
*/
class event
{
public:
/** The variant type for any possible event. */
using event_type = std::variant<
const_message_ptr, connected_event, connection_lost_event, disconnected_event,
shutdown_event>;
private:
event_type evt_{};
public:
/**
* Constructs an empty event.
* This shows as a message, but the message pointer is null.
*/
event() {}
/**
* Constructs an event from an event type variant.
* @param evt The event type variant.
*/
event(event_type evt) : evt_{std::move(evt)} {}
/**
* Constructs a message event.
* @param msg A shared message pointer.
*/
event(message_ptr msg) : evt_{std::move(msg)} {}
/**
* Constructs a message event.
* @param msg A shared const message pointer.
*/
event(const_message_ptr msg) : evt_{std::move(msg)} {}
/**
* Constructs a 'connected' event.
* @param evt A connected event.
*/
event(connected_event evt) : evt_{std::move(evt)} {}
/**
* Constructs a 'connection lost' event.
* @param evt A connection lost event.
*/
event(connection_lost_event evt) : evt_{std::move(evt)} {}
/**
* Constructs a 'disconnected' event.
* @param evt A disconnected event.
*/
event(disconnected_event evt) : evt_{std::move(evt)} {}
/**
* Constructs a 'shutdown' event.
* @param evt A shutdown event.
*/
event(shutdown_event evt) : evt_{std::move(evt)} {}
/**
* Copy constructor.
* @param evt The event to copy.
*/
event(const event& evt) : evt_{evt.evt_} {}
/**
* Move constructor.
* @param evt The event to move.
*/
event(event&& evt) : evt_{std::move(evt.evt_)} {}
/**
* Assignment from an event type variant.
* @param evt The event type variant.
* @return A reference to this object.
*/
event& operator=(event_type evt) {
evt_ = std::move(evt);
return *this;
}
/**
* Copy assignment.
* @param rhs The event to copy.
* @return A reference to this object.
*/
event& operator=(const event& rhs) {
if (&rhs != this)
evt_ = rhs.evt_;
return *this;
}
/**
* Move assignment.
* @param rhs The event to move.
* @return A reference to this object.
*/
event& operator=(event&& rhs) {
if (&rhs != this)
evt_ = std::move(rhs.evt_);
return *this;
}
/**
* Determines if this event is an incoming message.
* @return @em true if this event is an incoming message, @em false
* otherwise.
*/
bool is_message() const { return std::holds_alternative<const_message_ptr>(evt_); }
/**
* Determines if this event is a client (re)connection.
* @return @em true if this event is a client connection, @em false
* otherwise.
*/
bool is_connected() const { return std::holds_alternative<connected_event>(evt_); }
/**
* Determines if this event is a client connection lost.
* @return @em true if this event is a client connection lost, @em false
* otherwise.
*/
bool is_connection_lost() const {
return std::holds_alternative<connection_lost_event>(evt_);
}
/**
* Determines if this event is a client disconnected.
* @return @em true if this event is a client disconnected, @em false
* otherwise.
*/
bool is_disconnected() const { return std::holds_alternative<disconnected_event>(evt_); }
/**
* Determines if this event is an internal shutdown request.
* @return @em true if this event is a shutdown request, @em false
* otherwise.
*/
bool is_shutdown() const { return std::holds_alternative<disconnected_event>(evt_); }
/**
* Determines if this is any type of client disconnect or shutdown.
* @return @em true if this event is any type of client disconnect such
* as a 'connection lost', 'disconnected', or shutdown event.
*/
bool is_any_disconnect() const {
return std::holds_alternative<connection_lost_event>(evt_) ||
std::holds_alternative<disconnected_event>(evt_) ||
std::holds_alternative<shutdown_event>(evt_);
}
/**
* Gets the message from the event, iff this is a message event.
* @return A message pointer, if this is a message event.
* @throw std::bad_variant_access if this is not a 'message' event.
*/
const_message_ptr get_message() { return std::get<const_message_ptr>(evt_); }
/**
* Gets the underlying information for a disconnected event iff this is
* a 'disconnected' event.
* This contains the reason code and properties that the server sent in
* the DISCONNECT packet.
* @return The disconnected event object containing information about
* why the server disconnected.
* @throw std::bad_variant_access if this is not a 'disconnected' event.
*/
disconnected_event get_disconnected() { return std::get<disconnected_event>(evt_); }
/**
* Gets a pointer to the message in the event, iff this is a message
* event.
* @return A pointer to a message pointer, if this is a message event.
* Returns nulltr if this is not a message event.
*/
constexpr std::add_pointer_t<const_message_ptr> get_message_if() noexcept {
return std::get_if<const_message_ptr>(&evt_);
}
/**
* Gets a pointer the underlying information for a disconnected event,
* iff this is a 'disconnected' event.
* This contains the reason code and properties that the server sent in
* the DISCONNECT packet.
* @return The disconnected event object containing information about
* why the server disconnected.
* @throw std::bad_variant_access if this is not a 'disconnected' event.
*/
constexpr std::add_pointer_t<disconnected_event> get_disconnected_if() noexcept {
return std::get_if<disconnected_event>(&evt_);
}
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_event_h

261
include/mqtt/exception.h Normal file
View File

@ -0,0 +1,261 @@
/////////////////////////////////////////////////////////////////////////////
/// @file exception.h
/// Declaration of MQTT exception class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_exception_h
#define __mqtt_exception_h
#include <exception>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <vector>
#include "MQTTAsync.h"
#include "mqtt/types.h"
namespace mqtt {
/** Bring std::bad_cast into the mqtt namespace */
using bad_cast = std::bad_cast;
/////////////////////////////////////////////////////////////////////////////
/**
* Base mqtt::exception.
* This wraps the error codes which originate from the underlying C library.
*/
class exception : public std::runtime_error
{
protected:
/** The error return code from the C library */
int rc_;
/** The reason code from the server */
ReasonCode reasonCode_;
/** The error message from the C library */
string msg_;
/** See if the return code is actually a reason code error value */
static ReasonCode reason_code(int rc, ReasonCode reasonCode) {
if (reasonCode == ReasonCode::SUCCESS && rc >= ReasonCode::UNSPECIFIED_ERROR)
reasonCode = ReasonCode(rc);
return reasonCode;
}
public:
/**
* Creates an MQTT exception.
* @param rc The error return code from the C library.
*/
explicit exception(int rc) : exception(rc, error_str(rc)) {}
/**
* Creates an MQTT exception.
* @param rc The error return code from the C library.
* @param reasonCode The reason code from the server response.
*/
explicit exception(int rc, ReasonCode reasonCode)
: exception(rc, reasonCode, error_str(rc)) {}
/**
* Creates an MQTT exception.
* @param rc The error return code from the C library.
* @param msg The text message for the error.
*/
exception(int rc, const string& msg) : exception(rc, ReasonCode::SUCCESS, msg) {}
/**
* Creates an MQTT exception.
* @param rc The error return code from the C library.
* @param reasonCode The reason code from the server
* @param msg The text message for the error.
*/
exception(int rc, ReasonCode reasonCode, const string& msg)
: std::runtime_error(printable_error(rc, reasonCode, msg)),
rc_{rc},
reasonCode_{reason_code(rc, reasonCode)},
msg_{msg} {}
/**
* Gets an error message from an error code.
* @param rc The error code from the C lib
* @return A string explanation of the error
*/
static string error_str(int rc) {
const char* msg = ::MQTTAsync_strerror(rc);
return msg ? string(msg) : string();
}
/**
* Gets a string describing the MQTT v5 reason code.
* @param reasonCode The MQTT v5 reason code.
* @return A string describing the reason code.
*/
static string reason_code_str(int reasonCode) {
auto msg = ::MQTTReasonCode_toString(MQTTReasonCodes(reasonCode));
return (msg) ? string{msg} : string{};
}
/**
* Gets a detailed error message for an error code.
* @param rc The error code from the C lib
* @param reasonCode The MQTT v5 reason code
* @param msg An optional additional message. If none is provided, the
* error_str message is used.
* @return A string error message that includes the error code and an
* explanation message.
*/
static string printable_error(
int rc, ReasonCode reasonCode = ReasonCode::SUCCESS, const string& msg = string()
) {
reasonCode = reason_code(rc, reasonCode);
string s = "MQTT error [" + std::to_string(rc) + "]";
if (!msg.empty())
s += string(": ") + msg;
if (reasonCode != ReasonCode::SUCCESS)
s += string(". ") + reason_code_str(reasonCode);
return s;
}
/**
* Returns the return code for this exception.
*/
int get_return_code() const { return rc_; }
/**
* Gets a string of the error code.
* @return A string of the error code.
*/
string get_error_str() const { return error_str(rc_); }
/**
* Returns the reason code for this exception.
* For MQTT v3 connections, this is actually the return code.
*/
int get_reason_code() const { return reasonCode_; }
/**
* Gets a string for the reason code.
* @return A string for the reason code.
*/
string get_reason_code_str() const { return reason_code_str(reasonCode_); }
/**
* Returns the error message for this exception.
*/
string get_message() const { return msg_; }
/**
* Gets a string representation of this exception.
* @return A string representation of this exception.
*/
string to_string() const { return string(what()); }
};
/**
* Stream inserter writes a fairly verbose message
* @param os The stream.
* @param exc The exception to write.
* @return A reference to the stream.
*/
inline std::ostream& operator<<(std::ostream& os, const exception& exc) {
os << exc.what();
return os;
}
/////////////////////////////////////////////////////////////////////////////
/**
* Exception thrown when an expected server response is missing.
*/
class missing_response : public exception
{
public:
/**
* Create a missing response error.
* @param rsp A string for the type of response expected.
*/
missing_response(const string& rsp)
: exception(MQTTASYNC_FAILURE, "Missing " + rsp + " response") {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* A timeout exception, particularly from the synchronous client.
*/
class timeout_error : public exception
{
public:
/**
* Create a timeout error.
*/
timeout_error() : exception(MQTTASYNC_FAILURE, "Timeout") {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* This exception is thrown by the implementor of the persistence interface
* if there is a problem reading or writing persistent data.
*/
class persistence_exception : public exception
{
public:
/**
* Creates an MQTT persistence exception.
*/
persistence_exception() : exception(MQTTCLIENT_PERSISTENCE_ERROR) {}
/**
* Creates an MQTT persistence exception.
* @param code The error code from the C library.
*/
explicit persistence_exception(int code) : exception(code) {}
/**
* Creates an MQTT persistence exception.
* @param msg The text message for the error.
*/
explicit persistence_exception(const string& msg)
: exception(MQTTCLIENT_PERSISTENCE_ERROR, msg) {}
/**
* Creates an MQTT persistence exception.
* @param code The error code
* @param msg The text message for the error.
*/
persistence_exception(int code, const string& msg) : exception(code, msg) {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* Thrown when a client is not authorized to perform an operation, or if
* there is a problem with the security configuration.
*/
class security_exception : public exception
{
public:
/**
* Creates an MQTT security exception
* @param code The error code.
*/
explicit security_exception(int code) : exception(code) {}
/**
* Creates an MQTT security exception
* @param code The error code.
* @param msg The text message for the error.
*/
security_exception(int code, const string& msg) : exception(code, msg) {}
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_exception_h

44
include/mqtt/export.h Normal file
View File

@ -0,0 +1,44 @@
/////////////////////////////////////////////////////////////////////////////
/// @file export.h
/// Library symbol export definitions, primarily for Windows MSVC DLL's
/// @date November 20, 2023
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
* Frank Pagliughi - MQTT v5 support
*******************************************************************************/
#ifndef __mqtt_export_h
#define __mqtt_export_h
#if defined(_WIN32) && defined(_MSC_VER)
#if defined(PAHO_MQTTPP_EXPORTS)
#define PAHO_MQTTPP_EXPORT __declspec(dllexport)
#elif defined(PAHO_MQTTPP_IMPORTS)
#define PAHO_MQTTPP_EXPORT __declspec(dllimport)
#else
#define PAHO_MQTTPP_EXPORT
#endif
#else
#if defined(PAHO_MQTTPP_EXPORTS)
#define PAHO_MQTTPP_EXPORT __attribute__((visibility("default")))
#else
#define PAHO_MQTTPP_EXPORT
#endif
#endif
#endif // __mqtt_export_h

View File

@ -9,11 +9,11 @@
* Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com> * Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com>
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution. * and Eclipse Distribution License v1.0 which accompany this distribution.
* *
* The Eclipse Public License is available at * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php. * http://www.eclipse.org/org/documents/edl-v10.php.
* *
@ -24,9 +24,10 @@
#ifndef __mqtt_iaction_listener_h #ifndef __mqtt_iaction_listener_h
#define __mqtt_iaction_listener_h #define __mqtt_iaction_listener_h
#include <vector>
#include "MQTTAsync.h" #include "MQTTAsync.h"
#include "mqtt/types.h" #include "mqtt/types.h"
#include <vector>
namespace mqtt { namespace mqtt {
@ -48,25 +49,25 @@ class token;
class iaction_listener class iaction_listener
{ {
public: public:
/** Smart/shared pointer to an object of this class. */ /** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<iaction_listener>; using ptr_t = std::shared_ptr<iaction_listener>;
/** Smart/shared pointer to a const object of this class. */ /** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const iaction_listener>; using const_ptr_t = std::shared_ptr<const iaction_listener>;
/** /**
* Virtual base destructor. * Virtual base destructor.
*/ */
virtual ~iaction_listener() {} virtual ~iaction_listener() {}
/** /**
* This method is invoked when an action fails. * This method is invoked when an action fails.
* @param asyncActionToken * @param asyncActionToken The token
*/ */
virtual void on_failure(const token& asyncActionToken) =0; virtual void on_failure(const token& asyncActionToken) = 0;
/** /**
* This method is invoked when an action has completed successfully. * This method is invoked when an action has completed successfully.
* @param asyncActionToken * @param asyncActionToken The token
*/ */
virtual void on_success(const token& asyncActionToken) =0; virtual void on_success(const token& asyncActionToken) = 0;
}; };
/** Smart/shared pointer to an action listener */ /** Smart/shared pointer to an action listener */
@ -75,10 +76,7 @@ using iaction_listener_ptr = iaction_listener::ptr_t;
/** Smart/shared pointer to a const action listener */ /** Smart/shared pointer to a const action listener */
using const_iaction_listener_ptr = iaction_listener::const_ptr_t; using const_iaction_listener_ptr = iaction_listener::const_ptr_t;
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// end namespace mqtt } // namespace mqtt
}
#endif // __mqtt_iaction_listener_h
#endif // __mqtt_iaction_listener_h

View File

@ -0,0 +1,517 @@
/////////////////////////////////////////////////////////////////////////////
/// @file iasync_client.h
/// Implementation of the interface for the asynchronous clients,
/// 'iasync_client'
/// @date 25-Aug-2016
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_iasync_client_h
#define __mqtt_iasync_client_h
#include <vector>
#include "mqtt/callback.h"
#include "mqtt/connect_options.h"
#include "mqtt/delivery_token.h"
#include "mqtt/disconnect_options.h"
#include "mqtt/event.h"
#include "mqtt/exception.h"
#include "mqtt/iaction_listener.h"
#include "mqtt/iclient_persistence.h"
#include "mqtt/message.h"
#include "mqtt/subscribe_options.h"
#include "mqtt/token.h"
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Enables an application to communicate with an MQTT server using
* non-blocking methods.
*
* It provides applications a simple programming interface to all features
* of the MQTT version 3.1 specification including:
*
* @li connect
* @li publish
* @li subscribe
* @li unsubscribe
* @li disconnect
*/
class iasync_client
{
friend class token;
virtual void remove_token(token* tok) = 0;
public:
/** Type for a collection of QOS values */
using qos_collection = std::vector<int>;
/**
* Virtual destructor
*/
virtual ~iasync_client() {}
/**
* Connects to an MQTT server using the default options.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
virtual token_ptr connect() = 0;
/**
* Connects to an MQTT server using the provided connect options.
* @param options a set of connection parameters that override the
* defaults.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
virtual token_ptr connect(connect_options options) = 0;
/**
* Connects to an MQTT server using the specified options.
*
* @param options a set of connection parameters that override the
* defaults.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when the connect
* completes.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
virtual token_ptr connect(
connect_options options, void* userContext, iaction_listener& cb
) = 0;
/**
*
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when the connect completes.
* @return token used to track and wait for the connect to complete. The
* token will be passed to any callback that has been set.
* @throw exception for non security related problems
* @throw security_exception for security related problems
*/
virtual token_ptr connect(void* userContext, iaction_listener& cb) = 0;
/**
* Reconnects the client using options from the previous connect.
* The client must have previously called connect() for this to work.
* @return token used to track the progress of the reconnect.
*/
virtual token_ptr reconnect() = 0;
/**
* Disconnects from the server.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
virtual token_ptr disconnect() = 0;
/**
* Disconnects from the server.
* @param opts Options for disconnecting.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
virtual token_ptr disconnect(disconnect_options opts) = 0;
/**
* Disconnects from the server.
* @param timeout the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
virtual token_ptr disconnect(int timeout) = 0;
/**
* Disconnects from the server.
* @param timeout the amount of time in milliseconds to allow for
* existing work to finish before disconnecting. A value
* of zero or less means the client will not quiesce.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when the disconnect
* completes.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
virtual token_ptr disconnect(int timeout, void* userContext, iaction_listener& cb) = 0;
/**
* Disconnects from the server.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when the disconnect
* completes.
* @return token used to track and wait for the disconnect to complete.
* The token will be passed to any callback that has been set.
* @throw exception for problems encountered while disconnecting
*/
virtual token_ptr disconnect(void* userContext, iaction_listener& cb) = 0;
/**
* Returns the delivery token for the specified message ID.
* @return delivery_token
*/
virtual delivery_token_ptr get_pending_delivery_token(int msgID) const = 0;
/**
* Returns the delivery tokens for any outstanding publish operations.
* @return delivery_token[]
*/
virtual std::vector<delivery_token_ptr> get_pending_delivery_tokens() const = 0;
/**
* Returns the client ID used by this client.
* @return string
*/
virtual string get_client_id() const = 0;
/**
* Returns the address of the server used by this client.
*/
virtual string get_server_uri() const = 0;
/**
* Determines if this client is currently connected to the server.
*/
virtual bool is_connected() const = 0;
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload The bytes to use as the message payload
* @param n The number of bytes in the payload
* @param qos The Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained Whether or not this message should be retained by the
* server.
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(
string_ref topic, const void* payload, size_t n, int qos, bool retained,
const properties& props = properties()
) = 0;
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(string_ref topic, const void* payload, size_t n) = 0;
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload The bytes to use as the message payload
* @param n The number of bytes in the payload
* @param qos The Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained whether or not this message should be retained by the
* server.
* @param userContext Optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb The listener callback object
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(
string_ref topic, const void* payload, size_t n, int qos, bool retained,
void* userContext, iaction_listener& cb
) = 0;
/**
* Publishes a message to a topic on the server
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @param qos the Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained whether or not this message should be retained by the
* server.
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(
string_ref topic, binary_ref payload, int qos, bool retained,
const properties& props = properties()
) = 0;
/**
* Publishes a message to a topic on the server.
* @param topic The topic to deliver the message to
* @param payload the bytes to use as the message payload
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(string_ref topic, binary_ref payload) = 0;
/**
* Publishes a message to a topic on the server Takes an Message
* message and delivers it to the server at the requested quality of
* service.
* @param msg the message to deliver to the server
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(const_message_ptr msg) = 0;
/**
* Publishes a message to a topic on the server.
* @param msg the message to deliver to the server
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb optional listener that will be notified when message
* delivery has completed to the requested quality of service
* @return token used to track and wait for the publish to complete. The
* token will be passed to callback methods if set.
*/
virtual delivery_token_ptr publish(
const_message_ptr msg, void* userContext, iaction_listener& cb
) = 0;
/**
* Sets a callback listener to use for events that happen
* asynchronously.
* @param cb callback which will be invoked for certain asynchronous
* events
*/
virtual void set_callback(callback& cb) = 0;
/**
* Stops the callbacks.
*/
virtual void disable_callbacks() = 0;
/**
* Subscribe to a topic, which may include wildcards.
* @param topicFilter the topic to subscribe to, which can include
* wildcards.
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param opts The options for the subscription.
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr subscribe(
const string& topicFilter, int qos,
const subscribe_options& opts = subscribe_options(),
const properties& props = properties()
) = 0;
/**
* Subscribe to a topic, which may include wildcards.
* @param topicFilter the topic to subscribe to, which can include
* wildcards.
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param callback listener that will be notified when subscribe has
* completed
* @param opts The options for the subscription.
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr subscribe(
const string& topicFilter, int qos, void* userContext, iaction_listener& callback,
const subscribe_options& opts = subscribe_options(),
const properties& props = properties()
) = 0;
/**
* Subscribe to multiple topics, each of which may include wildcards.
* Provides an optimized way to subscribe to multiple topics compared to
* subscribing to each one individually.
* @param topicFilters one or more topics to subscribe to, which can
* include wildcards
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param opts A collection of subscription options (one for each
* topic)
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr subscribe(
const_string_collection_ptr topicFilters, const qos_collection& qos,
const std::vector<subscribe_options>& opts = std::vector<subscribe_options>(),
const properties& props = properties()
) = 0;
/**
* Subscribes to multiple topics, each of which may include wildcards.
* @param topicFilters one or more topics to subscribe to, which can
* include wildcards
* @param qos the maximum quality of service at which to subscribe.
* Messages published at a lower quality of service will be
* received at the published QoS. Messages published at a
* higher quality of service will be received using the QoS
* specified on the subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param callback listener that will be notified when subscribe has
* completed
* @param opts A collection of subscription options (one for each
* topic)
* @param props The MQTT v5 properties.
* @return token used to track and wait for the subscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr subscribe(
const_string_collection_ptr topicFilters, const qos_collection& qos,
void* userContext, iaction_listener& callback,
const std::vector<subscribe_options>& opts = std::vector<subscribe_options>(),
const properties& props = properties()
) = 0;
/**
* Requests the server unsubscribe the client from a topic.
* @param topicFilter the topic to unsubscribe from. It must match a
* topicFilter specified on an earlier subscribe.
* @param props The MQTT v5 properties.
* @return token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr unsubscribe(
const string& topicFilter, const properties& props = properties()
) = 0;
/**
* Requests the server unsubscribe the client from one or more topics.
* @param topicFilters one or more topics to unsubscribe from. Each
* topicFilter must match one specified on an
* earlier subscribe.
* @param props The MQTT v5 properties.
* @return token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr unsubscribe(
const_string_collection_ptr topicFilters, const properties& props = properties()
) = 0;
/**
* Requests the server unsubscribe the client from one or more topics.
* @param topicFilters one or more topics to unsubscribe from. Each
* topicFilter must match one specified on an
* earlier subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when unsubscribe has
* completed
* @param props The MQTT v5 properties.
* @return token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr unsubscribe(
const_string_collection_ptr topicFilters, void* userContext, iaction_listener& cb,
const properties& props = properties()
) = 0;
/**
* Requests the server unsubscribe the client from a topics.
* @param topicFilter the topic to unsubscribe from. It must match a
* topicFilter specified on an earlier subscribe.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb listener that will be notified when unsubscribe has
* completed.
* @param props The MQTT v5 properties.
* @return Token used to track and wait for the unsubscribe to complete.
* The token will be passed to callback methods if set.
*/
virtual token_ptr unsubscribe(
const string& topicFilter, void* userContext, iaction_listener& cb,
const properties& props = properties()
) = 0;
/**
* Start consuming messages.
* This initializes the client to receive messages through a queue that
* can be read synchronously.
*/
virtual void start_consuming() = 0;
/**
* Stop consuming messages.
* This shuts down the internal callback and discards any unread
* messages.
*/
virtual void stop_consuming() = 0;
/**
* This clears the consumer queue, discarding any pending event.
*/
virtual void clear_consumer() {}
/**
* Determines if the consumer queue has been closed.
* Once closed, any events in the queue can still be read, but no new
* events can be added to it.
* @return @true if the consumer queue has been closed, @false
* otherwise.
*/
virtual bool consumer_closed() noexcept { return false; }
/**
* Determines if the consumer queue is "done" (closed and empty).
* Once the queue is done, no more events can be added or removed from
* the queue.
* @return @true if the consumer queue is closed and empty, @false
* otherwise.
*/
virtual bool consumer_done() noexcept { return false; }
/**
* Gets the number of events available for immediate consumption.
* Note that this retrieves the number of "raw" events, not messages,
* e.g. may include a connected_event which is not returned by try_consume_message().
* When polling the queue from multiple threads, prefer using try_consume_event(),
* as the event count may change between checking the size and actual retrieval.
* @return the number of events in the queue.
*/
virtual std::size_t consumer_queue_size() const { return 0; }
/**
* Read the next message from the queue.
* This blocks until a new message arrives.
* @return The message and topic.
*/
virtual const_message_ptr consume_message() = 0;
/**
* Try to read the next message from the queue without blocking.
* @param msg Pointer to the value to receive the message
* @return @em true is a message was read, @em false if no message was
* available.
*/
virtual bool try_consume_message(const_message_ptr* msg) = 0;
/**
* Read the next event from the queue.
* This blocks until a new message arrives.
* @return The message and topic.
*/
virtual event consume_event() { return event{}; }
/**
* Try to read the next message from the queue without blocking.
* @param evt Pointer to the value to receive the event
* @return @em true is an event was received, @em false if no event was
* available.
*/
virtual bool try_consume_event(event* evt) {
(void)evt;
return false;
}
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_iasync_client_h

View File

@ -0,0 +1,153 @@
/////////////////////////////////////////////////////////////////////////////
/// @file iclient_persistence.h
/// Declaration of MQTT iclient_persistence interface
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_iclient_persistence_h
#define __mqtt_iclient_persistence_h
#include <vector>
#include "MQTTAsync.h"
#include "mqtt/buffer_view.h"
#include "mqtt/string_collection.h"
#include "mqtt/types.h"
namespace mqtt {
/**
* Allocate memory for use with user persistence.
*
* @param n The number of bytes for the buffer.
* @return A pointer to the allocated memory
*/
inline void* persistence_malloc(size_t n) { return MQTTAsync_malloc(n); }
/**
* Frees memory allocated with @ref persistence_malloc().
* @param p Pointer to a buffer obtained by persistence_malloc.
*/
inline void persistence_free(void* p) { MQTTAsync_free(p); }
/////////////////////////////////////////////////////////////////////////////
/**
* Represents a persistent data store, used to store outbound and inbound
* messages while they are in flight, enabling delivery to the QoS
* specified. You can specify an implementation of this interface using
* client::client(string, string, iclient_persistence), which the
* client will use to persist QoS 1 and 2 messages.
*
* If the methods defined throw the MqttPersistenceException then the state
* of the data persisted should remain as prior to the method being called.
* For example, if put(string, persistable) throws an exception at any
* point then the data will be assumed to not be in the persistent store.
* Similarly if remove(string) throws an exception then the data will be
* assumed to still be held in the persistent store.
*
* It is up to the persistence interface to log any exceptions or error
* information which may be required when diagnosing a persistence failure.
*/
class iclient_persistence
{
friend class async_client;
friend class mock_persistence;
/** Callbacks from the C library */
static int persistence_open(
void** handle, const char* clientID, const char* serverURI, void* context
);
static int persistence_close(void* handle);
static int persistence_put(
void* handle, char* key, int bufcount, char* buffers[], int buflens[]
);
static int persistence_get(void* handle, char* key, char** buffer, int* buflen);
static int persistence_remove(void* handle, char* key);
static int persistence_keys(void* handle, char*** keys, int* nkeys);
static int persistence_clear(void* handle);
static int persistence_containskey(void* handle, char* key);
public:
/** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<iclient_persistence>;
/** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const iclient_persistence>;
/**
* Virtual destructor.
*/
virtual ~iclient_persistence() {}
/**
* Initialize the persistent store.
* This uses the client ID and server name to create a unique location
* for the data store.
* @param clientId The identifier string for the client.
* @param serverURI The server to which the client is connected.
*/
virtual void open(const string& clientId, const string& serverURI) = 0;
/**
* Close the persistent store that was previously opened.
*/
virtual void close() = 0;
/**
* Clears persistence, so that it no longer contains any persisted data.
*/
virtual void clear() = 0;
/**
* Returns whether or not data is persisted using the specified key.
* @param key The key to find
* @return @em true if the key exists, @em false if not.
*/
virtual bool contains_key(const string& key) = 0;
/**
* Returns a collection of keys in this persistent data store.
* @return A collection of strings representing the keys in the store.
*/
virtual string_collection keys() const = 0;
/**
* Puts the specified data into the persistent store.
* @param key The key.
* @param bufs The data to store
*/
virtual void put(const string& key, const std::vector<string_view>& bufs) = 0;
/**
* Gets the specified data out of the persistent store.
* @param key The key
* @return A const view of the data associated with the key.
*/
virtual string get(const string& key) const = 0;
/**
* Remove the data for the specified key.
* @param key The key
*/
virtual void remove(const string& key) = 0;
};
/** Smart/shared pointer to a persistence client */
using iclient_persistence_ptr = iclient_persistence::ptr_t;
/** Smart/shared pointer to a persistence client */
using const_iclient_persistence_ptr = iclient_persistence::const_ptr_t;
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_iclient_persistence_h

514
include/mqtt/message.h Normal file
View File

@ -0,0 +1,514 @@
/////////////////////////////////////////////////////////////////////////////
/// @file message.h
/// Declaration of MQTT message class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
* Frank Pagliughi - MQTT v5 support (properties)
*******************************************************************************/
#ifndef __mqtt_message_h
#define __mqtt_message_h
#include <memory>
#include "MQTTAsync.h"
#include "mqtt/buffer_ref.h"
#include "mqtt/exception.h"
#include "mqtt/platform.h"
#include "mqtt/properties.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* An MQTT message holds everything required for an MQTT PUBLISH message.
* This holds the binary message payload, topic string, and all the
* additional meta-data for an MQTT message.
*
* The topic and payload buffers are kept as references to const data, so
* they can be reassigned as needed, but the buffers can not be updated
* in-place. Normally they would be created externally then copied or moved
* into the message. The library to transport the messages never touches the
* payloads or topics.
*
* This also means that message objects are fairly cheap to copy, since they
* don't copy the payloads. They simply copy the reference to the buffers.
* It is safe to pass these buffer references across threads since all
* references promise not to update the contents of the buffer.
*/
class message
{
public:
/** The default QoS for a message */
static constexpr int DFLT_QOS = 0;
/** The default retained flag */
static constexpr bool DFLT_RETAINED = false;
private:
/** Initializer for the C struct (from the C library) */
static constexpr MQTTAsync_message DFLT_C_STRUCT MQTTAsync_message_initializer;
/** A const string to use for references */
PAHO_MQTTPP_EXPORT static const string EMPTY_STR;
/** A const binary to use for references */
PAHO_MQTTPP_EXPORT static const binary EMPTY_BIN;
/** The underlying C message struct */
MQTTAsync_message msg_{DFLT_C_STRUCT};
/** The topic that the message was (or should be) sent on. */
string_ref topic_;
/** The message payload - an arbitrary binary blob. */
binary_ref payload_;
/** The properties for the message */
properties props_;
/** The client has special access. */
friend class async_client;
/**
* Set the dup flag in the underlying message
* @param dup Whether to set the dup flag.
*/
void set_duplicate(bool dup) { msg_.dup = to_int(dup); }
public:
/** Smart/shared pointer to this class. */
using ptr_t = std::shared_ptr<message>;
/** Smart/shared pointer to this class. */
using const_ptr_t = std::shared_ptr<const message>;
/**
* Constructs a message with an empty payload, and all other values set
* to defaults.
*/
message() {}
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
* @param props The MQTT v5 properties for the message.
*/
message(
string_ref topic, const void* payload, size_t len, int qos, bool retained,
const properties& props = properties()
);
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
*/
message(string_ref topic, const void* payload, size_t len)
: message(std::move(topic), payload, len, DFLT_QOS, DFLT_RETAINED) {}
/**
* Constructs a message from a byte buffer.
* Note that the payload accepts copy or move semantics.
* @param topic The message topic
* @param payload A byte buffer to use as the message payload.
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
* @param props The MQTT v5 properties for the message.
*/
message(
string_ref topic, binary_ref payload, int qos, bool retained,
const properties& props = properties()
);
/**
* Constructs a message from a byte buffer.
* Note that the payload accepts copy or move semantics.
* @param topic The message topic
* @param payload A byte buffer to use as the message payload.
*/
message(string_ref topic, binary_ref payload)
: message(std::move(topic), std::move(payload), DFLT_QOS, DFLT_RETAINED) {}
/**
* Constructs a message as a copy of the message structure.
* @param topic The message topic
* @param cmsg A "C" MQTTAsync_message structure.
*/
message(string_ref topic, const MQTTAsync_message& cmsg);
/**
* Constructs a message as a copy of the other message.
* @param other The message to copy into this one.
*/
message(const message& other);
/**
* Moves the other message to this one.
* @param other The message to move into this one.
*/
message(message&& other);
/**
* Destroys a message and frees all associated resources.
*/
~message() {}
/**
* Constructs a message with the specified values.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
* @param props The MQTT v5 properties for the message.
*/
static ptr_t create(
string_ref topic, const void* payload, size_t len, int qos, bool retained,
const properties& props = properties()
) {
return std::make_shared<message>(
std::move(topic), payload, len, qos, retained, props
);
}
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
*/
static ptr_t create(string_ref topic, const void* payload, size_t len) {
return std::make_shared<message>(
std::move(topic), payload, len, DFLT_QOS, DFLT_RETAINED
);
}
/**
* Constructs a message from a byte buffer.
* Note that the payload accepts copy or move semantics.
* @param topic The message topic
* @param payload A byte buffer to use as the message payload.
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
* @param props The MQTT v5 properties for the message.
*/
static ptr_t create(
string_ref topic, binary_ref payload, int qos, bool retained,
const properties& props = properties()
) {
return std::make_shared<message>(
std::move(topic), std::move(payload), qos, retained, props
);
}
/**
* Constructs a message from a byte buffer.
* Note that the payload accepts copy or move semantics.
* @param topic The message topic
* @param payload A byte buffer to use as the message payload.
*/
static ptr_t create(string_ref topic, binary_ref payload) {
return std::make_shared<message>(
std::move(topic), std::move(payload), DFLT_QOS, DFLT_RETAINED
);
}
/**
* Constructs a message as a copy of the C message struct.
* @param topic The message topic
* @param msg A "C" MQTTAsync_message structure.
*/
static ptr_t create(string_ref topic, const MQTTAsync_message& msg) {
return std::make_shared<message>(std::move(topic), msg);
}
/**
* Copies another message to this one.
* @param rhs The other message.
* @return A reference to this message.
*/
message& operator=(const message& rhs);
/**
* Moves another message to this one.
* @param rhs The other message.
* @return A reference to this message.
*/
message& operator=(message&& rhs);
/**
* Expose the underlying C struct for the unit tests.
*/
#if defined(UNIT_TESTS)
const MQTTAsync_message& c_struct() const { return msg_; }
#endif
/**
* Sets the topic string.
* @param topic The topic on which the message is published.
*/
void set_topic(string_ref topic) {
topic_ = topic ? std::move(topic) : string_ref(string());
}
/**
* Gets the topic reference for the message.
* @return The topic reference for the message.
*/
const string_ref& get_topic_ref() const { return topic_; }
/**
* Gets the topic for the message.
* @return The topic string for the message.
*/
const string& get_topic() const { return topic_ ? topic_.str() : EMPTY_STR; }
/**
* Clears the payload, resetting it to be empty.
*/
void clear_payload();
/**
* Gets the payload reference.
*/
const binary_ref& get_payload_ref() const { return payload_; }
/**
* Gets the payload
*/
const binary& get_payload() const { return payload_ ? payload_.str() : EMPTY_BIN; }
/**
* Gets the payload as a string
*/
const string& get_payload_str() const { return payload_ ? payload_.str() : EMPTY_STR; }
/**
* Returns the quality of service for this message.
* @return The quality of service for this message.
*/
int get_qos() const { return msg_.qos; }
/**
* Returns whether or not this message might be a duplicate of one which
* has already been received.
* @return true this message might be a duplicate of one which
* has already been received, false otherwise
*/
bool is_duplicate() const { return to_bool(msg_.dup); }
/**
* Returns whether or not this message should be/was retained by the
* server.
* @return true if this message should be/was retained by the
* server, false otherwise.
*/
bool is_retained() const { return to_bool(msg_.retained); }
/**
* Sets the payload of this message to be the specified buffer.
* Note that this accepts copy or move operations:
* set_payload(buf);
* set_payload(std::move(buf));
* @param payload A buffer to use as the message payload.
*/
void set_payload(binary_ref payload);
/**
* Sets the payload of this message to be the specified byte array.
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
*/
void set_payload(const void* payload, size_t n) {
set_payload(binary_ref(static_cast<const binary_ref::value_type*>(payload), n));
}
/**
* Sets the quality of service for this message.
* @param qos The integer Quality of Service for the message
*/
void set_qos(int qos) {
validate_qos(qos);
msg_.qos = qos;
}
/**
* Determines if the QOS value is a valid one.
* @param qos The QOS value.
* @throw std::invalid_argument If the qos value is invalid.
*/
static void validate_qos(int qos) {
if (qos < 0 || qos > 2)
throw exception(MQTTASYNC_BAD_QOS, "Bad QoS");
}
/**
* Whether or not the publish message should be retained by the broker.
* @param retained @em true if the message should be retained by the
* broker, @em false if not.
*/
void set_retained(bool retained) { msg_.retained = to_int(retained); }
/**
* Gets the properties in the message.
* @return A const reference to the properties in the message.
*/
const properties& get_properties() const { return props_; }
/**
* Sets the properties in the message.
* @param props The properties to place into the message.
*/
void set_properties(const properties& props) {
props_ = props;
msg_.properties = props_.c_struct();
}
/**
* Moves the properties into the message.
* @param props The properties to move into the message.
*/
void set_properties(properties&& props) {
props_ = std::move(props);
msg_.properties = props_.c_struct();
}
/**
* Returns a string representation of this messages payload.
* @return A string representation of this messages payload.
*/
string to_string() const { return get_payload_str(); }
};
/** Smart/shared pointer to a message */
using message_ptr = message::ptr_t;
/** Smart/shared pointer to a const message */
using const_message_ptr = message::const_ptr_t;
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
* @param props The MQTT v5 properties for the message.
*/
inline message_ptr make_message(
string_ref topic, const void* payload, size_t len, int qos, bool retained,
const properties& props = properties()
) {
return mqtt::message::create(std::move(topic), payload, len, qos, retained, props);
}
/**
* Constructs a message with the specified array as a payload, and all
* other values set to defaults.
* @param topic The message topic
* @param payload the bytes to use as the message payload
* @param len the number of bytes in the payload
*/
inline message_ptr make_message(string_ref topic, const void* payload, size_t len) {
return mqtt::message::create(std::move(topic), payload, len);
}
/**
* Constructs a message with the specified values.
* @param topic The message topic
* @param payload A buffer to use as the message payload.
* @param qos The quality of service for the message.
* @param retained Whether the message should be retained by the broker.
*/
inline message_ptr make_message(
string_ref topic, binary_ref payload, int qos, bool retained,
const properties& props = properties()
) {
return mqtt::message::create(std::move(topic), std::move(payload), qos, retained, props);
}
/**
* Constructs a message with the specified buffer as a payload, and
* all other values set to defaults.
* @param topic The message topic
* @param payload A string to use as the message payload.
*/
inline message_ptr make_message(string_ref topic, binary_ref payload) {
return mqtt::message::create(std::move(topic), std::move(payload));
}
/////////////////////////////////////////////////////////////////////////////
/**
* Class to build messages.
*/
class message_ptr_builder
{
/** The underlying message */
message_ptr msg_;
public:
/** This class */
using self = message_ptr_builder;
/**
* Default constructor.
*/
message_ptr_builder() : msg_{std::make_shared<message>()} {}
/**
* Sets the topic string.
* @param topic The topic on which the message is published.
*/
auto topic(string_ref topic) -> self& {
msg_->set_topic(topic);
return *this;
}
/**
* Sets the payload of this message to be the specified buffer.
* Note that this accepts copy or move operations:
* set_payload(buf);
* set_payload(std::move(buf));
* @param payload A buffer to use as the message payload.
*/
auto payload(binary_ref payload) -> self& {
msg_->set_payload(payload);
return *this;
}
/**
* Sets the payload of this message to be the specified byte array.
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
*/
auto payload(const void* payload, size_t n) -> self& {
msg_->set_payload(payload, n);
return *this;
}
/**
* Sets the quality of service for this message.
* @param qos The integer Quality of Service for the message
*/
auto qos(int qos) -> self& {
msg_->set_qos(qos);
return *this;
}
/**
* Whether or not the publish message should be retained by the broker.
* @param on @em true if the message should be retained by the broker, @em
* false if not.
*/
auto retained(bool on) -> self& {
msg_->set_retained(on);
return *this;
}
/**
* Sets the properties for the disconnect message.
* @param props The properties for the disconnect message.
*/
auto properties(mqtt::properties&& props) -> self& {
msg_->set_properties(std::move(props));
return *this;
}
/**
* Sets the properties for the disconnect message.
* @param props The properties for the disconnect message.
*/
auto properties(const mqtt::properties& props) -> self& {
msg_->set_properties(props);
return *this;
}
/**
* Finish building the options and return them.
* @return The option struct as built.
*/
message_ptr finalize() { return msg_; }
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_message_h

29
include/mqtt/platform.h Normal file
View File

@ -0,0 +1,29 @@
/////////////////////////////////////////////////////////////////////////////
/// @file platform.h
/// Paho MQTT platform-specific code
/// @date Nov 19, 2023
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_platform_h
#define __mqtt_platform_h
#include "mqtt/export.h"
#endif // __mqtt_platform_h

533
include/mqtt/properties.h Normal file
View File

@ -0,0 +1,533 @@
/////////////////////////////////////////////////////////////////////////////
/// @file properties.h
/// Declaration of MQTT properties class
/// @date July 7, 2019
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2019-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_properties_h
#define __mqtt_properties_h
extern "C" {
#include "MQTTProperties.h"
}
#include <initializer_list>
#include <iostream>
#include <map>
#include <stdexcept>
#include <string_view>
#include <tuple>
#include <typeinfo>
#include "mqtt/buffer_ref.h"
#include "mqtt/exception.h"
#include "mqtt/platform.h"
#include "mqtt/types.h"
namespace mqtt {
/** A pair of strings as a tuple. */
using string_pair = std::tuple<string, string>;
/////////////////////////////////////////////////////////////////////////////
/**
* A single MQTT v5 property.
*/
class property
{
/** The underlying Paho C property struct. */
MQTTProperty prop_;
// Make a deep copy of the property struct into this one.
// For string properties, this allocates memory and copied the string(s)
void copy(const MQTTProperty& other);
friend class properties;
property() {}
public:
/**
* The integer codes for the different v5 properties.
*/
enum code {
PAYLOAD_FORMAT_INDICATOR = 1,
MESSAGE_EXPIRY_INTERVAL = 2,
CONTENT_TYPE = 3,
RESPONSE_TOPIC = 8,
CORRELATION_DATA = 9,
SUBSCRIPTION_IDENTIFIER = 11,
SESSION_EXPIRY_INTERVAL = 17,
ASSIGNED_CLIENT_IDENTIFIER = 18,
SERVER_KEEP_ALIVE = 19,
AUTHENTICATION_METHOD = 21,
AUTHENTICATION_DATA = 22,
REQUEST_PROBLEM_INFORMATION = 23,
WILL_DELAY_INTERVAL = 24,
REQUEST_RESPONSE_INFORMATION = 25,
RESPONSE_INFORMATION = 26,
SERVER_REFERENCE = 28,
REASON_STRING = 31,
RECEIVE_MAXIMUM = 33,
TOPIC_ALIAS_MAXIMUM = 34,
TOPIC_ALIAS = 35,
MAXIMUM_QOS = 36,
RETAIN_AVAILABLE = 37,
USER_PROPERTY = 38,
MAXIMUM_PACKET_SIZE = 39,
WILDCARD_SUBSCRIPTION_AVAILABLE = 40,
SUBSCRIPTION_IDENTIFIERS_AVAILABLE = 41,
SHARED_SUBSCRIPTION_AVAILABLE = 42
};
/** The names of the different types of properties */
PAHO_MQTTPP_EXPORT static const std::map<code, std::string_view> TYPE_NAME;
/**
* Create a numeric property.
* This can be a byte, or 2-byte, 4-byte, or variable byte integer.
* @param c The property code
* @param val The integer value for the property
*/
property(code c, int32_t val);
/**
* Create a numeric property.
* This can be a byte, or 2-byte, 4-byte, or variable byte integer.
* @param c The property code
* @param val The integer value for the property
*/
property(code c, uint32_t val) : property(c, int32_t(val)) {}
/**
* Create a string or binary property.
* @param c The property code
* @param val The value for the property
*/
property(code c, string_ref val);
/**
* Create a string pair property.
* @param c The property code
* @param name The string name for the property
* @param val The string value for the property
*/
property(code c, string_ref name, string_ref val);
/**
* Creates a property from a C struct.
* @param cprop A C struct for a property list.
*/
explicit property(const MQTTProperty& cprop) { copy(cprop); }
/**
* Moves a C struct into this property list.
* This takes ownership of any memory that the C struct is holding.
* @param cprop A C struct for a property list.
*/
explicit property(MQTTProperty&& cprop) : prop_(cprop) {
memset(&cprop, 0, sizeof(MQTTProperty));
}
/**
* Copy constructor
* @param other The other property to copy into this one.
*/
property(const property& other) { copy(other.prop_); }
/**
* Move constructor.
* @param other The other property that is moved into this one.
*/
property(property&& other);
/**
* Destructor
*/
~property();
/**
* Copy assignment.
* @param rhs Another property list to copy into this one.
* @return A reference to this object.
*/
property& operator=(const property& rhs);
/**
* Move assignment.
* @param rhs Another property list to move into this one.
* @return A reference to this object.
*/
property& operator=(property&& rhs);
/**
* Gets the underlying C property struct.
* @return A const reference to the underlying C property
* struct.
*/
const MQTTProperty& c_struct() const { return prop_; }
/**
* Gets the property type (identifier).
* @return The code for the property type.
*/
code type() const { return code(prop_.identifier); }
/**
* Gets a printable name for the property type.
* @return A printable name for the property type.
*/
std::string_view type_name() const;
/**
* Gets the typeid for the value contained in the property.
* @return The typeid for the value contained in the property.
*/
const std::type_info& value_type_id();
};
std::ostream& operator<<(std::ostream& os, const property& prop);
/**
* Extracts the value from the property as the specified type.
* @return The value from the property as the specified type.
*/
template <typename T>
inline T get(const property&) {
throw bad_cast();
}
/**
* Extracts the value from the property as an unsigned 8-bit integer.
* @return The value from the property as an unsigned 8-bit integer.
*/
template <>
inline uint8_t get<uint8_t>(const property& prop) {
return uint8_t(prop.c_struct().value.byte);
}
/**
* Extracts the value from the property as an unsigned 16-bit integer.
* @return The value from the property as an unsigned 16-bit integer.
*/
template <>
inline uint16_t get<uint16_t>(const property& prop) {
return uint16_t(prop.c_struct().value.integer2);
}
/**
* Extracts the value from the property as a signed 16-bit integer.
* @return The value from the property as a signed 16-bit integer.
* @deprecated All integer properties are unsigned. Use
* `get<uint16_t>()`
*/
template <>
[[deprecated("Integer properties are unsigned. Use get<uint16_t>()")]] inline int16_t
get<int16_t>(const property& prop) {
return int16_t(prop.c_struct().value.integer2);
}
/**
* Extracts the value from the property as an unsigned 32-bit integer.
* @return The value from the property as an unsigned 32-bit integer.
*/
template <>
inline uint32_t get<uint32_t>(const property& prop) {
return uint32_t(prop.c_struct().value.integer4);
}
/**
* Extracts the value from the property as a signed 32-bit integer.
* @return The value from the property as a signed 32-bit integer.
* @deprecated All integer properties are unsigned. Use
* `get<uint32_t>()`
*/
template <>
[[deprecated("Integer properties are unsigned. Use get<uint32_t>()")]] inline int32_t
get<int32_t>(const property& prop) {
return int32_t(prop.c_struct().value.integer4);
}
/**
* Extracts the value from the property as a string.
* @return The value from the property as a string.
*/
template <>
inline string get<string>(const property& prop) {
return (!prop.c_struct().value.data.data)
? string()
: string(prop.c_struct().value.data.data, prop.c_struct().value.data.len);
}
/**
* Extracts the value from the property as a pair of strings.
* @return The value from the property as a pair of strings.
*/
template <>
inline string_pair get<string_pair>(const property& prop) {
string name =
(!prop.c_struct().value.data.data)
? string()
: string(prop.c_struct().value.data.data, prop.c_struct().value.data.len);
string value =
(!prop.c_struct().value.value.data)
? string()
: string(prop.c_struct().value.value.data, prop.c_struct().value.value.len);
return std::make_tuple(std::move(name), std::move(value));
}
/////////////////////////////////////////////////////////////////////////////
/**
* MQTT v5 property list.
*
* A collection of properties that can be added to outgoing packets or
* retrieved from incoming packets.
*/
class properties
{
/** The default C struct */
static constexpr MQTTProperties DFLT_C_STRUCT MQTTProperties_initializer;
/** The underlying C properties struct */
MQTTProperties props_{DFLT_C_STRUCT};
template <typename T>
friend T get(const properties& props, property::code propid, size_t idx);
template <typename T>
friend T get(const properties& props, property::code propid);
public:
/** A const iterator for the properties list */
class const_iterator
{
const MQTTProperty* curr_;
mutable property prop_;
friend properties;
const_iterator(const MQTTProperty* curr) : curr_{curr} {}
public:
/**
* Gets a reference to the current value.
* @return A reference to the current value.
*/
const property& operator*() const {
prop_ = property{*curr_};
return prop_;
}
/**
* Postfix increment operator.
* @return An iterator pointing to the previous matching item.
*/
const_iterator operator++(int) noexcept {
auto tmp = *this;
curr_++;
return tmp;
}
/**
* Prefix increment operator.
* @return An iterator pointing to the next matching item.
*/
const_iterator& operator++() noexcept {
++curr_;
return *this;
}
/**
* Compares two iterators to see if they don't refer to the same
* node.
*
* @param other The other iterator to compare against this one.
* @return @em true if they don't match, @em false if they do
*/
bool operator!=(const const_iterator& other) const noexcept {
return curr_ != other.curr_;
}
};
/**
* Default constructor.
* Creates an empty properties list.
*/
properties() {}
/**
* Copy constructor.
* @param other The property list to copy.
*/
properties(const properties& other) : props_(::MQTTProperties_copy(&other.props_)) {}
/**
* Move constructor.
* @param other The property list to move to this one.
*/
properties(properties&& other) : props_(other.props_) {
std::memset(&other.props_, 0, sizeof(MQTTProperties));
}
/**
* Creates a list of properties from a C struct.
* @param cprops The c struct of properties
*/
properties(const MQTTProperties& cprops) { props_ = ::MQTTProperties_copy(&cprops); }
/**
* Constructs from a list of property objects.
* @param props An initializer list of property objects.
*/
properties(std::initializer_list<property> props);
/**
* Destructor.
*/
~properties() { ::MQTTProperties_free(&props_); }
/**
* Gets a reference to the underlying C properties structure.
* @return A const reference to the underlying C properties structure.
*/
const MQTTProperties& c_struct() const { return props_; }
/**
* Copy assignment.
* @param rhs The other property list to copy into this one
* @return A reference to this object.
*/
properties& operator=(const properties& rhs);
/**
* Move assignment.
* @param rhs The property list to move to this one.
* @return A reference to this object.
*/
properties& operator=(properties&& rhs);
/**
* Determines if the property list is empty.
* @return @em true if there are no properties in the list, @em false if
* the list contains any items.
*/
bool empty() const { return props_.count == 0; }
/**
* Gets the property at the specified index in the collection.
* @param i The index
* @return The property at the specified index.
*/
const property operator[](size_t i) const { return property{props_.array[i]}; }
/**
* Gets the property at the specified index in the collection.
* @param i The index
* @return The property at the specified index.
*/
const property at(size_t i) const {
if (i < size_t(props_.count))
return property{props_.array[i]};
throw std::out_of_range{"property index"};
}
/**
* Gets the numbers of property items in the list.
* @return The number of property items in the list.
*/
size_t size() const { return size_t(props_.count); }
/**
* Gets a const iterator to the full collection of properties.
* @return A const iterator to the full collection of properties.
*/
const_iterator begin() const { return const_iterator{props_.array}; }
/**
* Gets a const iterator to the full collection of properties.
* @return A const iterator to the full collection of properties.
*/
const_iterator cbegin() const { return begin(); }
/**
* Gets a const iterator to the end of the collection of properties.
* @return A const iterator to the end of collection of properties.
*/
const_iterator end() const { return const_iterator{props_.array + size()}; }
/**
* Gets a const iterator to the end of the collection of properties.
* @return A const iterator to the end of collection of properties.
*/
const_iterator cend() const { return end(); }
/**
* Adds a property to the list.
* @param prop The property to add to the list.
*/
void add(const property& prop) { ::MQTTProperties_add(&props_, &prop.c_struct()); }
/**
* Removes all the items from the property list.
*/
void clear() { ::MQTTProperties_free(&props_); }
/**
* Determines if the list contains a specific property.
* @param propid The property ID (code).
* @return @em true if the list contains the property, @em false if not.
*/
bool contains(property::code propid) const {
return ::MQTTProperties_hasProperty(
const_cast<MQTTProperties*>(&props_), MQTTPropertyCodes(propid)
) != 0;
}
/**
* Get the number of properties in the list with the specified property
* ID.
*
* Most properties can exist only once. User properties and subscription
* ID's can exist more than once.
*
* @param propid The property ID (code).
* @return The number of properties in the list with the specified ID.
*/
size_t count(property::code propid) const {
return size_t(
::MQTTProperties_propertyCount(
const_cast<MQTTProperties*>(&props_), MQTTPropertyCodes(propid)
)
);
}
/**
* Gets the property with the specified ID.
*
* @param propid The property ID (code).
* @param idx Which instance of the property to retrieve, if there are
* more than one.
* @return The requested property
*/
property get(property::code propid, size_t idx = 0) const;
};
// --------------------------------------------------------------------------
/**
* Retrieves a single value from a property list for when there may be
* multiple identical property ID's.
* @tparam T The type of the value to retrieve
* @param props The property list
* @param propid The property ID code for the desired value.
* @param idx Index of the desired property ID
* @return The requested value of type T
*/
template <typename T>
inline T get(const properties& props, property::code propid, size_t idx) {
MQTTProperty* prop = MQTTProperties_getPropertyAt(
const_cast<MQTTProperties*>(&props.c_struct()), MQTTPropertyCodes(propid), int(idx)
);
if (!prop)
throw bad_cast();
return get<T>(property(*prop));
}
/**
* Retrieves a single value from a property list.
* @tparam T The type of the value to retrieve
* @param props The property list
* @param propid The property ID code for the desired value.
* @return The requested value of type T
*/
template <typename T>
inline T get(const properties& props, property::code propid) {
return get<T>(props, propid, 0);
}
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_properties_h

110
include/mqtt/reason_code.h Normal file
View File

@ -0,0 +1,110 @@
/////////////////////////////////////////////////////////////////////////////
/// @file reason_code.h
///
/// MQTT v5 reason codes for the Paho MQTT C++ library.
///
/// @date July 5, 2024
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __reason_code_h
#define __reason_code_h
#include <iostream>
#include <string>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* The MQTT v5 Reason Codes.
*/
enum ReasonCode {
SUCCESS = 0,
NORMAL_DISCONNECTION = 0,
GRANTED_QOS_0 = 0,
GRANTED_QOS_1 = 1,
GRANTED_QOS_2 = 2,
DISCONNECT_WITH_WILL_MESSAGE = 4,
NO_MATCHING_SUBSCRIBERS = 16,
NO_SUBSCRIPTION_FOUND = 17,
CONTINUE_AUTHENTICATION = 24,
RE_AUTHENTICATE = 25,
UNSPECIFIED_ERROR = 128,
MALFORMED_PACKET = 129,
PROTOCOL_ERROR = 130,
IMPLEMENTATION_SPECIFIC_ERROR = 131,
UNSUPPORTED_PROTOCOL_VERSION = 132,
CLIENT_IDENTIFIER_NOT_VALID = 133,
BAD_USER_NAME_OR_PASSWORD = 134,
NOT_AUTHORIZED = 135,
SERVER_UNAVAILABLE = 136,
SERVER_BUSY = 137,
BANNED = 138,
SERVER_SHUTTING_DOWN = 139,
BAD_AUTHENTICATION_METHOD = 140,
KEEP_ALIVE_TIMEOUT = 141,
SESSION_TAKEN_OVER = 142,
TOPIC_FILTER_INVALID = 143,
TOPIC_NAME_INVALID = 144,
PACKET_IDENTIFIER_IN_USE = 145,
PACKET_IDENTIFIER_NOT_FOUND = 146,
RECEIVE_MAXIMUM_EXCEEDED = 147,
TOPIC_ALIAS_INVALID = 148,
PACKET_TOO_LARGE = 149,
MESSAGE_RATE_TOO_HIGH = 150,
QUOTA_EXCEEDED = 151,
ADMINISTRATIVE_ACTION = 152,
PAYLOAD_FORMAT_INVALID = 153,
RETAIN_NOT_SUPPORTED = 154,
QOS_NOT_SUPPORTED = 155,
USE_ANOTHER_SERVER = 156,
SERVER_MOVED = 157,
SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 158,
CONNECTION_RATE_EXCEEDED = 159,
MAXIMUM_CONNECT_TIME = 160,
SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED = 161,
WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 162,
// This is not a protocol code; used internally by the library (obsolete)
MQTTPP_V3_CODE = 0
};
/**
* Get the string representation of the reason code.
*
* @param reasonCode An MQTT v5 reason code.
* @return The string representation of the reason code.
*/
std::string to_string(ReasonCode reasonCode);
/**
* ostream inserter for reason codes
*
* @param os The output stream
* @param reasonCode The reason code.
*
* @return Reference to the output stream
*/
std::ostream& operator<<(std::ostream& os, ReasonCode reasonCode);
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __reason_code_h

View File

@ -0,0 +1,306 @@
/////////////////////////////////////////////////////////////////////////////
/// @file response_options.h
/// Implementation of the class 'response_options'
/// @date 26-Aug-2019
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2019-2025 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_response_options_h
#define __mqtt_response_options_h
#include "MQTTAsync.h"
#include "mqtt/delivery_token.h"
#include "mqtt/token.h"
#include "subscribe_options.h"
namespace mqtt {
class token_test;
/////////////////////////////////////////////////////////////////////////////
// response_options
/////////////////////////////////////////////////////////////////////////////
/**
* The response options for various asynchronous calls.
*
* This is an internal data structure, only used within the library.
* Therefore it is not totally fleshed out, but rather only exposes the
* functionality currently required by the library.
*
* Note, too, in the C lib, this became a place to add MQTT v5 options for
* the outgoing calls without breaking the API, so is also referred to as the
* "call options".
*/
class response_options
{
/** The underlying C structure */
MQTTAsync_responseOptions opts_ MQTTAsync_responseOptions_initializer;
/** The token to which we are connected */
token::weak_ptr_t tok_;
/** Packet Properties (Subscribe/Unsubscribe) */
properties props_;
/** A list of subscription options for subscribe-many */
std::vector<MQTTSubscribe_options> subOpts_;
/** The client has special access */
friend class async_client;
/** Update the underlying C struct to match our data */
void update_c_struct();
public:
/**
* Create an empty response object.
* @param mqttVersion The MQTT version for the response.
*/
explicit response_options(int mqttVersion = MQTTVERSION_DEFAULT) {
set_mqtt_version(mqttVersion);
}
/**
* Creates a response object with the specified callbacks.
* @param tok A token to be used as the context.
* @param mqttVersion The MQTT version for the response.
*/
response_options(const token_ptr& tok, int mqttVersion = MQTTVERSION_DEFAULT);
/**
* Copy constructor.
* @param other The other options to copy to this one.
*/
response_options(const response_options& other);
/**
* Move constructor.
* @param other The other options to move into this one.
*/
response_options(response_options&& other);
/**
* Copy operator.
* @param rhs The other options to copy to this one.
*/
response_options& operator=(const response_options& rhs);
/**
* Move operator.
* @param rhs The other options to move into this one.
*/
response_options& operator=(response_options&& rhs);
/**
* Expose the underlying C struct for the unit tests.
*/
#if defined(UNIT_TESTS)
const auto& c_struct() const { return opts_; }
#endif
/**
* Sets the MQTT protocol version used for the response.
* This sets up proper callbacks for MQTT v5 or versions prior to that.
* @param mqttVersion The MQTT version used by the connection.
*/
void set_mqtt_version(int mqttVersion);
/**
* Sets the callback context to a generic token.
* @param tok The token to be used as the callback context.
*/
void set_token(const token_ptr& tok);
/**
* Gets the properties for the response options.
*/
const properties& get_properties() const { return props_; }
/**
* Sets the properties for the response options.
* @param props The properties for the response options.
*/
void set_properties(const properties& props) {
props_ = props;
opts_.properties = props_.c_struct();
}
/**
* Moves the properties for the response options.
* @param props The properties to move into the response options.
*/
void set_properties(properties&& props) {
props_ = std::move(props);
opts_.properties = props_.c_struct();
}
/**
* Gets the options for a single topic subscription.
* @return The subscribe options.
*/
subscribe_options get_subscribe_options() const {
return subscribe_options{opts_.subscribeOptions};
}
/**
* Sets the options for a multi-topic subscription.
* @return The vector of the subscribe options.
*/
std::vector<subscribe_options> get_subscribe_many_options() const;
/**
* Sets the options for a single topic subscription.
* @param opts The subscribe options.
*/
void set_subscribe_options(const subscribe_options& opts);
/**
* Sets the options for a multi-topic subscription.
* @param opts A vector of the subscribe options.
*/
void set_subscribe_many_options(const std::vector<subscribe_options>& opts);
/**
* Sets the options for a multi-topic subscription.
* @param opts A vector of the subscribe options.
* @sa set_subscribe_options
*/
void set_subscribe_options(const std::vector<subscribe_options>& opts) {
set_subscribe_many_options(opts);
}
};
/////////////////////////////////////////////////////////////////////////////
/**
* Class to build response options.
*/
class response_options_builder
{
/** The underlying options */
response_options opts_;
public:
/** This class */
using self = response_options_builder;
/**
* Default constructor.
*/
explicit response_options_builder(int mqttVersion = MQTTVERSION_DEFAULT)
: opts_(mqttVersion) {}
/**
* Sets the MQTT protocol version used for the response.
* This sets up proper callbacks for MQTT v5 or versions prior to that.
* @param mqttVersion The MQTT version used by the connection.
*/
auto mqtt_version(int mqttVersion) -> self& {
opts_.set_mqtt_version(mqttVersion);
return *this;
}
/**
* Sets the callback context to a generic token.
* @param tok The token to be used as the callback context.
*/
auto token(const token_ptr& tok) -> self& {
opts_.set_token(tok);
return *this;
}
/**
* Sets the properties for the response options.
* @param props The properties for the response options.
*/
auto properties(mqtt::properties&& props) -> self& {
opts_.set_properties(std::move(props));
return *this;
}
/**
* Sets the properties for the disconnect message.
* @param props The properties for the disconnect message.
*/
auto properties(const mqtt::properties& props) -> self& {
opts_.set_properties(props);
return *this;
}
/**
* Sets the options for a single topic subscription.
* @param opts The subscribe options.
*/
auto subscribe_opts(const subscribe_options& opts) -> self& {
opts_.set_subscribe_options(opts);
return *this;
}
/**
* Sets the options for a multi-topic subscription.
* @param opts A vector of the subscribe options.
*/
auto subscribe_many_opts(const std::vector<subscribe_options>& opts) -> self& {
opts_.set_subscribe_options(opts);
return *this;
}
/**
* Sets the options for a multi-topic subscription.
* @param opts A vector of the subscribe options.
*/
auto subscribe_opts(const std::vector<subscribe_options>& opts) -> self& {
opts_.set_subscribe_options(opts);
return *this;
}
/**
* Finish building the response options and return them.
* @return The response option struct as built.
*/
response_options finalize() { return opts_; }
};
/////////////////////////////////////////////////////////////////////////////
// delivery_response_options
/////////////////////////////////////////////////////////////////////////////
/**
* The response options for asynchronous calls targeted at delivery.
* Each of these objects is tied to a specific delivery_token.
*/
class delivery_response_options
{
/** The underlying C structure */
MQTTAsync_responseOptions opts_;
/** The delivery token to which we are connected */
delivery_token::weak_ptr_t dtok_;
/** The client has special access */
friend class async_client;
public:
/**
* Create an empty delivery response object.
*/
delivery_response_options(int mqttVersion = MQTTVERSION_DEFAULT);
/**
* Creates a response object tied to the specific delivery token.
* @param dtok A delivery token to be used as the context.
* @param mqttVersion The MQTT version for the response
*/
delivery_response_options(
const delivery_token_ptr& dtok, int mqttVersion = MQTTVERSION_DEFAULT
);
/**
* Expose the underlying C struct for the unit tests.
*/
#if defined(UNIT_TESTS)
const MQTTAsync_responseOptions& c_struct() const { return opts_; }
#endif
/**
* Sets the callback context to a delivery token.
* @param dtok The delivery token to be used as the callback context.
*/
void set_token(const delivery_token_ptr& dtok) {
dtok_ = dtok;
opts_.context = dtok.get();
}
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_response_options_h

View File

@ -0,0 +1,205 @@
/////////////////////////////////////////////////////////////////////////////
/// @file server_response.h
/// Declaration of MQTT server response classes.
/// @date July 26, 2019
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2019-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_server_response_h
#define __mqtt_server_response_h
#include <iostream>
#include "MQTTAsync.h"
#include "mqtt/properties.h"
#include "mqtt/types.h"
namespace mqtt {
/**
* Base class for responses from the server.
*/
class server_response
{
/** The properties from the acknowledge */
properties props_;
public:
/**
* Creates a response with empty property list.
*/
server_response() {}
/**
* Creates a server response with the specified properties.
* @param props The properties in the response.
*/
server_response(const properties& props) : props_{props} {}
/**
* Creates a server response with the specified properties.
* @param props The properties in the response.
*/
server_response(properties&& props) : props_{std::move(props)} {}
/**
* Virtual destructor.
*/
virtual ~server_response() {}
/**
* Gets the properties from the response.
* @return The properties from the response.
*/
const properties& get_properties() const noexcept { return props_; }
};
/**
* Response for a connect request.
*/
class connect_response : public server_response
{
/** The connection string of the server */
string serverURI_;
/** The version of MQTT being used */
int mqttVersion_;
/** The session present flag returned from the server */
bool sessionPresent_;
friend class token;
/**
* Create v5 connect response.
* @param rsp The v5 response struct from the C lib
*/
connect_response(const MQTTAsync_successData5* rsp);
/**
* Create v3 connect response.
* @param rsp The v3 response struct from the C lib
*/
connect_response(const MQTTAsync_successData* rsp);
public:
/**
* Gets the URI of the broker to which we connected.
* @return The URI of the broker.
*/
string get_server_uri() const { return serverURI_; }
/**
* Gets the MQTT version for the connection.
* @return The MQTT version for the connection.
*/
int get_mqtt_version() const { return mqttVersion_; }
/**
* Determines whether a session already existed for this client on the
* server.
* This tells whether the server has a persistent session stored for the
* client, given the ClientID specified in the connect message.
* @return Whether a session already existed for this client on the server.
*/
bool is_session_present() const { return sessionPresent_; }
};
/**
* Response for a subscribe request.
*
* This contains the information returned from the broker in the SUBACK
* packet. It gives information about the granted Qos for each topc in the
* subscribe request.
*
* @li MQTT v3: These are return "codes" with the value 0-2 for each of the
* topic filters sent in the subscribe message.
* @li MQTT v5 These are reason codes, with one for each of the topics sent
* in the subscribe message. On success, the values are the same as for
* MQTT v3: the granted QoS 0-2. For errors, each could be an error code
* with a value >= 0x80, as described in the MQTT v5 spec: (not
* authorized, quota exceeded, etc).
*/
struct subscribe_response : public server_response
{
/** The reason/result code for each topic request. */
std::vector<ReasonCode> reasonCodes_;
friend class token;
/**
* Create v5 subscribe response.
* @param rsp The v5 response struct from the C lib
*/
subscribe_response(MQTTAsync_successData5* rsp);
/**
* Create v3 subscribe response.
* @param n The number of subscription topics
* @param rsp The v3 response struct from the C lib
*/
subscribe_response(size_t n, MQTTAsync_successData* rsp);
public:
/**
* Gets the reason codes from the server response.
*
* On a subscribe ack there is a return/reason code for each topic that
* was sent in the subscribe packet. Each tells the granted QoS
* for the corresponding topic.
*
* For MQTT v5 values over 0x80 are error codes as described in the MQTT
* v5 spec.
*
* @return A collection of return/reason codes corresponding to
* subscribing each topic. On success, this is the
* granted QoS for each topic. On failure it is the
* reason for the failure.
*/
const std::vector<ReasonCode>& get_reason_codes() const { return reasonCodes_; }
};
/**
* Response for unsubscribe messages.
*/
class unsubscribe_response : public server_response
{
/** The reason/result code for each topic request. */
std::vector<ReasonCode> reasonCodes_;
friend class token;
/**
* Create v5 unsubscribe response.
* @param rsp The v5 response struct from the C lib
*/
unsubscribe_response(MQTTAsync_successData5* rsp);
/**
* Create v3 subscribe response.
* The broker doesn't return any useful information for an unsubscribe
* in MQTT v3.
*/
unsubscribe_response(MQTTAsync_successData*) {}
public:
/**
* Gets the reason codes from the server response.
* On an unsubscribe ack there is a reason code for each topic
* that was sent in the unsubscribe packet. Each tells the
* result of unsubscribing to the corresponding topic.
* @return A collection of return codes corresponding to
* unsubscribing each topic.
*/
const std::vector<ReasonCode>& get_reason_codes() const { return reasonCodes_; }
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_server_response_h

520
include/mqtt/ssl_options.h Normal file
View File

@ -0,0 +1,520 @@
/////////////////////////////////////////////////////////////////////////////
/// @file ssl_options.h
/// Declaration of MQTT ssl_options class
/// @date Jul 7, 2016
/// @author Frank Pagliughi, Guilherme Ferreira
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2016-2024 Frank Pagliughi <fpagliughi@mindspring.com>
* Copyright (c) 2016 Guilherme Ferreira <guilherme.maciel.ferreira@gmail.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Guilherme Ferreira - initial implementation and documentation
* Frank Pagliughi - added copy & move operations
* Frank Pagliughi - upgraded compatibility to Paho C 1.3
*******************************************************************************/
#ifndef __mqtt_ssl_options_h
#define __mqtt_ssl_options_h
#include <functional>
#include <vector>
#include "MQTTAsync.h"
#include "mqtt/message.h"
#include "mqtt/platform.h"
#include "mqtt/topic.h"
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Holds the set of SSL options for connection.
*/
class ssl_options
{
public:
/** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<ssl_options>;
/** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const ssl_options>;
/** Unique pointer to an object of this class. */
using unique_ptr_t = std::unique_ptr<ssl_options>;
/** Handler type for error message callbacks */
using error_handler = std::function<void(const string& errMsg)>;
/**
* Handler type for TLS-PSK option callback.
* On success, the callback should return the length of the PSK (in
* bytes). On failure, it should throw or return zero.
*/
using psk_handler = std::function<unsigned(
const string& hint, char* identity, size_t max_identity_len, unsigned char* psk,
size_t max_psk_len
)>;
private:
/** The default C struct */
static constexpr MQTTAsync_SSLOptions DFLT_C_STRUCT MQTTAsync_SSLOptions_initializer;
/** The underlying C SSL options */
MQTTAsync_SSLOptions opts_{DFLT_C_STRUCT};
/**
* The file containing the public digital certificates trusted by
* the client.
*/
string trustStore_;
/** The file containing the public certificate chain of the client. */
string keyStore_;
/** The file containing the client's private key. */
string privateKey_;
/** The password to load the client's privateKey if encrypted. */
string privateKeyPassword_;
/** Path to a directory containing CA certificates in PEM format */
string caPath_;
/**
* The list of cipher suites that the client will present to the
* server during the SSL handshake.
*/
string enabledCipherSuites_;
/** Error message callback handler */
error_handler errHandler_;
/** PSK callback handler */
psk_handler pskHandler_;
/** ALPN protocol list, in wire format */
std::basic_string<unsigned char> protos_;
/** Callbacks from the C library */
static int on_error(const char* str, size_t len, void* context);
static unsigned on_psk(
const char* hint, char* identity, unsigned int max_identity_len, unsigned char* psk,
unsigned int max_psk_len, void* context
);
/** The connect options has special access */
friend class connect_options;
/**
* Gets a pointer to the C-language NUL-terminated strings for the
* struct.
* @note In the SSL options, by default, the Paho C treats nullptr char
* arrays as unset values, so we keep that semantic and only set those
* char arrays if the string is non-empty.
* @param str The C++ string object.
* @return Pointer to a NUL terminated string. This is only valid until
* the next time the string is updated.
*/
const char* c_str(const string& str) { return str.empty() ? nullptr : str.c_str(); }
/**
* Updates the underlying C structure to match our strings.
*/
void update_c_struct();
public:
/**
* Constructs a new MqttConnectOptions object using the default values.
*/
ssl_options() {}
/**
* Argument constructor.
* @param trustStore The file containing the public digital certificates
* trusted by the client.
* @param keyStore The file containing the public certificate chain of the
* client.
* @param privateKey The file containing the client's private key.
* @param privateKeyPassword The password to load the client's privateKey
* if encrypted.
* @param enabledCipherSuites The list of cipher suites that the client
* will present to the server during the SSL handshake.
* @param enableServerCertAuth True/False option to enable verification of
* the server certificate
* @param alpnProtos The ALPN protocols to try.
*/
ssl_options(
const string& trustStore, const string& keyStore, const string& privateKey,
const string& privateKeyPassword, const string& enabledCipherSuites,
bool enableServerCertAuth,
const std::vector<string> alpnProtos = std::vector<string>()
);
/**
* Argument constructor.
* @param trustStore The file containing the public digital certificates
* trusted by the client.
* @param keyStore The file containing the public certificate chain of
* the client.
* @param privateKey The file containing the client's private key.
* @param privateKeyPassword The password to load the client's
* privateKey if encrypted.
* @param caPath The name of a directory containing CA certificates in
* PEM format.
* @param enabledCipherSuites The list of cipher suites that the client
* will present to the server during the SSL
* handshake.
* @param enableServerCertAuth True/False option to enable verification
* of the server certificate
* @param alpnProtos The ALPN protocols to try.
*/
ssl_options(
const string& trustStore, const string& keyStore, const string& privateKey,
const string& privateKeyPassword, const string& caPath,
const string& enabledCipherSuites, bool enableServerCertAuth,
const std::vector<string> alpnProtos = std::vector<string>()
);
/**
* Copy constructor.
* @param opt The other options to copy.
*/
ssl_options(const ssl_options& opt);
/**
* Move constructor.
* @param opt The other options to move to this one.
*/
ssl_options(ssl_options&& opt);
/**
* Copy assignment.
* @param opt The other options to copy.
* @return A reference to this object.
*/
ssl_options& operator=(const ssl_options& opt);
/**
* Move assignment.
* @param opt The other options to move to this one.
* @return A reference to this object.
*/
ssl_options& operator=(ssl_options&& opt);
/**
* Expose the underlying C struct for the unit tests.
*/
#if defined(UNIT_TESTS)
const MQTTAsync_SSLOptions& c_struct() const { return opts_; }
#endif
/**
* Returns the file containing the public digital certificates trusted by
* the client.
* @return string
*/
string get_trust_store() const { return trustStore_; }
/**
* Returns the file containing the public certificate chain of the client.
* @return string
*/
string get_key_store() const { return keyStore_; }
/**
* Gets the name of file containing the client's private key.
* @return The name of file containing the client's private key.
*/
string get_private_key() const { return privateKey_; }
/**
* Gets the password to load the client's privateKey if encrypted.
* @return The password to load the client's privateKey if encrypted.
*/
string get_private_key_password() const { return privateKeyPassword_; }
/**
* Returns the list of cipher suites that the client will present to the
* server during the SSL handshake.
* @return string
*/
string get_enabled_cipher_suites() const { return enabledCipherSuites_; }
/**
* Returns the true/false to enable verification of the server certificate .
* @return bool
*/
bool get_enable_server_cert_auth() const { return to_bool(opts_.enableServerCertAuth); }
/**
* Sets the file containing the public digital certificates trusted by
* the client.
* @param trustStore The file in PEM format containing the public
* digital certificates trusted by the client.
*/
void set_trust_store(const string& trustStore);
/**
* Sets the file containing the public certificate chain of the client.
* @param keyStore The file in PEM format containing the public
* certificate chain of the client. It may also include
* the client's private key.
*/
void set_key_store(const string& keyStore);
/**
* Sets the file containing the client's private key.
* @param privateKey If not included in the sslKeyStore, this is the
* file in PEM format containing the client's private
* key.
*/
void set_private_key(const string& privateKey);
/**
* Sets the password to load the client's privateKey if encrypted.
* @param privateKeyPassword The password to load the privateKey if
* encrypted.
*/
void set_private_key_password(const string& privateKeyPassword);
/**
* Sets the list of cipher suites that the client will present to the server
* during the SSL handshake.
* @param enabledCipherSuites The list of cipher suites that the client
* will present to the server during the SSL
* handshake. For a full explanation of the
* cipher list format, please see the OpenSSL
* on-line documentation:
* https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html
* If this setting is omitted, its default
* value will be "ALL", that is, all the
* cipher suites -excluding those offering no
* encryption- will be considered. This
* setting can be used to set an SSL
* anonymous connection (empty string value,
* for instance).
*/
void set_enabled_cipher_suites(const string& enabledCipherSuites);
/**
* Enables or disables verification of the server certificate.
* @param enableServerCertAuth enable/disable verification of the server
* certificate
*/
void set_enable_server_cert_auth(bool enableServerCertAuth);
/**
* Gets the requested SSL/TLS version.
* @return The requested SSL/TLS version.
*/
int get_ssl_version() const { return opts_.sslVersion; }
/**
* Set the SSL/TLS version to use.
*
* @param ver The desired SSL/TLS version. Specify one of:
* @li MQTT_SSL_VERSION_DEFAULT (0)
* @li MQTT_SSL_VERSION_TLS_1_0 (1)
* @li MQTT_SSL_VERSION_TLS_1_1 (2)
* @li MQTT_SSL_VERSION_TLS_1_2 (3)
*/
void set_ssl_version(int ver) { opts_.sslVersion = ver; }
/**
* Determines whether it will carry out post-connect checks, including
* that a certificate matches the given host name.
* @return Whether it will carry out post-connect checks.
*/
bool get_verify() const { return to_bool(opts_.verify); }
/**
* Sets whether it should carry out post-connect checks, including that
* a certificate matches the given host name.
* @param v Whether it should carry out post-connect checks.
*/
void set_verify(bool v) { opts_.verify = to_int(v); }
/**
* Gets the path to a directory containing CA certificates in PEM
* format.
*
* @return Path to a directory containing CA certificates in PEM format,
* if set. If this isn't set, returns an empty string.
*/
string get_ca_path() const { return caPath_; }
string ca_path() const { return caPath_; }
/**
* Sets the path to a directory containing CA certificates in PEM
* format.
*
* @param path Path to a directory containing CA certificates in PEM
* format.
*/
void set_ca_path(const string& path);
void ca_path(const string& path) { set_ca_path(path); }
/**
* Registers the error message callback handler.
* @param cb The callback to receive error messages.
*/
void set_error_handler(error_handler cb);
/**
* Registers a callback handler to set the TLS-PSK options.
* See: OpenSSL SSL_CTX_set_psk_client_callback()
* @param cb The callback.
*/
void set_psk_handler(psk_handler cb);
/**
* Gets the list of supported ALPN protocols.
* @return A vector containing the supported ALPN protocols.
*/
std::vector<string> get_alpn_protos() const;
/**
* Sets the list of supported ALPN protocols.
* See:
* https://www.openssl.org/docs/man1.1.0/man3/SSL_CTX_set_alpn_protos.html
* @param protos The list of ALPN protocols to be negotiated.
*/
void set_alpn_protos(const std::vector<string>& protos);
};
/**
* Shared pointer to the ssl options class.
*/
using ssl_options_ptr = ssl_options::ptr_t;
/**
* Unique pointer to the ssl options class.
*/
using ssl_options_unique_ptr = ssl_options::unique_ptr_t;
/////////////////////////////////////////////////////////////////////////////
/**
* Class to build the SSL options for connections.
*/
class ssl_options_builder
{
/** The underlying options */
ssl_options opts_;
public:
/** This class */
using self = ssl_options_builder;
/**
* Default constructor.
*/
ssl_options_builder() {}
/**
* Sets the file containing the public digital certificates trusted by
* the client.
* @param store The file in PEM format containing the public digital
* certificates trusted by the client.
*/
auto trust_store(const string& store) -> self& {
opts_.set_trust_store(store);
return *this;
}
/**
* Sets the file containing the public certificate chain of the client.
* @param store The file in PEM format containing the public certificate
* chain of the client. It may also include the client's
* private key.
*/
auto key_store(const string& store) -> self& {
opts_.set_key_store(store);
return *this;
}
/**
* Sets the file containing the client's private key.
* @param key If not included in the sslKeyStore, this is the file in
* PEM format containing the client's private key.
*/
auto private_key(const string& key) -> self& {
opts_.set_private_key(key);
return *this;
}
/**
* Sets the password to load the client's privateKey if encrypted.
* @param passwd The password to load the privateKey if encrypted.
*/
auto private_keypassword(const string& passwd) -> self& {
opts_.set_private_key_password(passwd);
return *this;
}
/**
* Sets the list of cipher suites that the client will present to the server
* during the SSL handshake.
* @param suites The list of cipher suites that the client will present to
* the server during the SSL handshake. For a full
* explanation of the cipher list format, please see the
* OpenSSL on-line documentation:
* http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT
* If this setting is omitted, its default value will be
* "ALL", that is, all the cipher suites -excluding those
* offering no encryption- will be considered. This setting
* can be used to set an SSL anonymous connection (empty
* string value, for instance).
*/
auto enabled_cipher_suites(const string& suites) -> self& {
opts_.set_enabled_cipher_suites(suites);
return *this;
}
/**
* Enables or disables verification of the server certificate.
* @param on enable/disable verification of the server certificate
*/
auto enable_server_cert_auth(bool on) -> self& {
opts_.set_enable_server_cert_auth(on);
return *this;
}
/**
* Set the SSL/TLS version to use.
*
* @param ver The desired SSL/TLS version. Specify one of:
* @li MQTT_SSL_VERSION_DEFAULT (0)
* @li MQTT_SSL_VERSION_TLS_1_0 (1)
* @li MQTT_SSL_VERSION_TLS_1_1 (2)
* @li MQTT_SSL_VERSION_TLS_1_2 (3)
*/
auto ssl_version(int ver) -> self& {
opts_.set_ssl_version(ver);
return *this;
}
/**
* Sets whether it should carry out post-connect checks, including that
* a certificate matches the given host name.
* @param on Whether it should carry out post-connect checks.
*/
auto verify(bool on = true) -> self& {
opts_.set_verify(on);
return *this;
}
/**
* Sets the path to a directory containing CA certificates in PEM format.
* @param path Path to a directory containing CA certificates in PEM
* format.
*/
auto ca_path(const string& path) -> self& {
opts_.ca_path(path);
return *this;
}
/**
* Registers an error callback handler.
* @param cb The callback to receive error messages.
*/
auto error_handler(ssl_options::error_handler cb) -> self& {
opts_.set_error_handler(cb);
return *this;
}
/**
* Registers a callback handler to set the TLS-PSK options.
* See: OpenSSL SSL_CTX_set_psk_client_callback()
* @param cb The callback.
*/
auto psk_handler(ssl_options::psk_handler cb) -> self& {
opts_.set_psk_handler(cb);
return *this;
}
/**
* Sets the list of supported ALPN protocols.
* @param protos The list of ALPN protocols to be negotiated.
*/
auto alpn_protos(const std::vector<string>& protos) -> self& {
opts_.set_alpn_protos(protos);
return *this;
}
/**
* Finish building the options and return them.
* @return The option struct as built.
*/
ssl_options finalize() { return opts_; }
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_ssl_options_h

View File

@ -0,0 +1,393 @@
/////////////////////////////////////////////////////////////////////////////
/// @file string_collection.h
/// Definition of the string_collection class for the Paho MQTT C++ library.
/// @date April 23, 2017
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2017-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_string_collection_h
#define __mqtt_string_collection_h
#include <map>
#include <memory>
#include <vector>
#include "MQTTAsync.h"
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* Type for a collection of strings, such as MQTT topics.
*
* This acts like a standard collection of strings but carries an array of
* pointers to the C strings for easy interactions with the Paho C library.
*/
class string_collection
{
public:
/** The type for the collection of strings */
using collection_type = std::vector<string>;
/** Iterator over const items */
using const_iterator = collection_type::const_iterator;
/** Smart/shared pointer to an object of this type */
using ptr_t = std::shared_ptr<string_collection>;
/** Smart/shared pointer to a const object of this type */
using const_ptr_t = std::shared_ptr<const string_collection>;
private:
/** The type for the array of C pointers */
using c_arr_type = std::vector<const char*>;
/**
* The collection of strings.
*/
collection_type coll_;
/**
* A collection of pointers to NUL-terminated C strings.
* This is what is required by the Paho C library, and thus the lifetime
* of the pointers will remain consistent with the lifetime of the
* object. The value is kept consistent with the current stringss and
* updated whenever strings are added or removed.
*/
c_arr_type cArr_;
/**
* Updated the cArr_ object to agree with the values in coll_
* This should be called any time the coll_ variable is modified
* <i>in any way</i>.
*/
void update_c_arr();
public:
/**
* Construct an empty string collection.
*/
string_collection() = default;
/**
* Construct a collection initially containing a single string.
* @param str The string
*/
string_collection(const string& str);
/**
* Construct a collection initially containing a single string.
* @param str The string
*/
string_collection(string&& str);
/**
* Constructs a string collection by copying a vector of strings.
* @param vec A vector of strings.
*/
string_collection(const collection_type& vec);
/**
* Constructs a string collection by moving a vector of strings.
* @param vec A vector of strings.
*/
string_collection(collection_type&& vec);
/**
* Copy constructor.
* @param coll An existing string collection.
*/
string_collection(const string_collection& coll);
/**
* Move constructor.
* @param coll An existing string collection.
*/
string_collection(string_collection&& coll) = default;
/**
* Construct a string collection from an initialization list of strings.
* @param sl An initialization list of strings.
*/
string_collection(std::initializer_list<string> sl);
/**
* Construct a string collection from an initialization list of C string
* pointers.
* @param sl An initialization list of C character arrays.
*/
string_collection(std::initializer_list<const char*> sl);
/**
* Create an empty string collection on the heap.
* @return A smart/shared pointer to a string collection.
*/
static ptr_t create(const string& str) {
return std::make_shared<string_collection>(str);
}
/**
* Create a string collection on the heap, initially containing a single
* string.
* @param str The string
* @return A smart/shared pointer to a string collection.
*/
static ptr_t create(string&& str) { return std::make_shared<string_collection>(str); }
/**
* Creates a string collection on the heap by copying a vector of
* strings.
* @param vec A vector of strings.
*/
static ptr_t create(const collection_type& vec) {
return std::make_shared<string_collection>(vec);
}
/**
* Creates a string collection on the heap by copying a vector of
* strings.
* @param vec A vector of strings.
* @return A smart/shared pointer to a string collection.
*/
static ptr_t create(collection_type&& vec) {
return std::make_shared<string_collection>(vec);
}
/**
* Create a string collection on the heap from an initialization list of
* strings.
* @param sl An initialization list of strings.
* @return A smart/shared pointer to a string collection.
*/
static ptr_t create(std::initializer_list<string> sl) {
return std::make_shared<string_collection>(sl);
}
/**
* Create a string collection on the heap from an initialization list of
* C string pointers.
* @param sl An initialization list of C character arrays.
* @return A smart/shared pointer to a string collection.
*/
static ptr_t create(std::initializer_list<const char*> sl) {
return std::make_shared<string_collection>(sl);
}
/**
* Copy assignment.
* Copy another string collection to this one.
* @param coll A string collection
* @return A reference to this collection.
*/
string_collection& operator=(const string_collection& coll);
/**
* Move assignment.
* Move another string collection to this one.
* @param coll A string collection
* @return A reference to this collection.
*/
string_collection& operator=(string_collection&& coll) = default;
/**
* Gets a const iterator to the beginning of the collection.
* @return A const iterator to the beginning of the collection.
*/
const_iterator begin() const { return coll_.begin(); }
/**
* Gets a const iterator to the end of the collection.
* @return A const iterator to the end of the collection.
*/
const_iterator end() const { return coll_.end(); }
/**
* Gets a const iterator to the beginning of the collection.
* @return A const iterator to the beginning of the collection.
*/
const_iterator cbegin() const { return coll_.cbegin(); }
/**
* Gets a const iterator to the end of the collection.
* @return A const iterator to the end of the collection.
*/
const_iterator cend() const { return coll_.cend(); }
/**
* Determines if the collection is empty.
* @return @em true if the collection is empty, @em false if not.
*/
bool empty() const { return coll_.empty(); }
/**
* Gets the number of strings in the collection.
* @return The number of strings in the collection.
*/
size_t size() const { return coll_.size(); }
/**
* Copies a string onto the back of the collection.
* @param str A string.
*/
void push_back(const string& str);
/**
* Moves a string onto the back of the collection.
* @param str A string.
*/
void push_back(string&& str);
/**
* Removes all the strings from the collection.
*/
void clear();
/**
* Gets the n'th string in the collection.
* @param i Index to the desired string.
* @return A const reference to the string.
*/
const string& operator[](size_t i) const { return coll_[i]; }
/**
* Gets a pointer to an array of NUL-terminated C string pointers.
* This is the collection type supported by the underlying Paho C
* library. The returned pointer is guaranteed valid so long as the
* object is not updated. The return value may change if the object is
* modified, so the application should not cache the return value, but
* rather request the value when needed.
* @return pointer to an array of NUL-terminated C string pointers of
* the C++ strings in the object.
*
*/
char* const* c_arr() const { return (char* const*)cArr_.data(); }
};
/////////////////////////////////////////////////////////////////////////////
/** Smart/shared pointer to a string collection */
using string_collection_ptr = string_collection::ptr_t;
/** Smart/shared pointer to a const string_collection */
using const_string_collection_ptr = string_collection::const_ptr_t;
/////////////////////////////////////////////////////////////////////////////
/**
* A collection of name/value string pairs.
*/
class name_value_collection
{
/** The type for the collection of name/value pairs */
using collection_type = std::map<string, string>;
/** The type for the C pointers to pass to Paho C */
using c_arr_type = std::vector<MQTTAsync_nameValue>;
/**
* The name/value pairs.
*/
collection_type map_;
/**
* A collection of pairs of NUL-terminated C strings.
*/
c_arr_type cArr_;
/**
* Updated the cArr_ object to agree with the values in coll_
* This should be called any time the coll_ variable is modified
* <i>in any way</i>.
*/
void update_c_arr();
public:
/** Smart/shared pointer to an object of this type */
using ptr_t = std::shared_ptr<name_value_collection>;
/** Smart/shared pointer to a const object of this type */
using const_ptr_t = std::shared_ptr<const name_value_collection>;
/** The type of the string/string pair of values */
using value_type = collection_type::value_type;
/**
* Default constructor for an empty collection.
*/
name_value_collection() = default;
/**
* Creates a name/value collection from an underlying STL collection.
* @param map The collection of name/value pairs.
*/
name_value_collection(const collection_type& map) : map_(map) { update_c_arr(); }
/**
* Creates a name/value collection from an underlying STL collection.
* @param map The collection of name/value pairs.
*/
name_value_collection(collection_type&& map) : map_(std::move(map)) { update_c_arr(); }
/**
* Copy constructor.
* @param other Another collection of name/value pairs.
*/
name_value_collection(const name_value_collection& other) : map_(other.map_) {
update_c_arr();
}
/**
* Move constructor.
* @param other Another collection of name/value pairs
*/
name_value_collection(name_value_collection&& other) = default;
/**
* Constructs the collection with an initializer list.
*
* This works identically to initializing a std::map<> with string/tring
* pairs.
*
* @param init Initializer list to construct the members of the
* collection.
*/
name_value_collection(std::initializer_list<value_type> init) : map_{init} {
update_c_arr();
}
/**
* Copy assignment.
* @param other Another collection of name/value pairs.
*/
name_value_collection& operator=(const name_value_collection& other) {
map_ = other.map_;
update_c_arr();
return *this;
}
/**
* Move constructor.
* @param other Another collection of name/value pairs
*/
name_value_collection& operator=(name_value_collection&& other) = default;
/**
* Determines if the collection is empty.
* @return @em true if the container is empty, @em false if it contains
* one or more items.
*/
bool empty() const { return map_.empty(); }
/**
* Gets the number of name/value pairs in the collection.
* @return The number of name/value pairs in the collection.
*/
size_t size() const { return map_.size(); }
/**
* Removes all items from the collection.
*/
void clear() {
map_.clear();
update_c_arr();
}
/**
* Inserts a name/value pair into the collection.
* @param nvpair The name/value string pair.
* @return @em true if the inert happened, @em false if not.
*/
bool insert(const value_type& nvpair) {
if (map_.insert(nvpair).second) {
update_c_arr();
return true;
}
return false;
}
/**
* Gets a pointer to an array of NUL-terminated C string pointer pairs.
* This is a collection type supported by the underlying Paho C
* library. The returned pointer is guaranteed valid so long as the
* object is not updated. The return value may change if the object is
* modified, so the application should not cache the return value, but
* rather request the value when needed.
* @return pointer to an array of NUL-terminated C string pointer pairs
* for name/values. The array is terminated by a NULL/NULL pair.
*/
const MQTTAsync_nameValue* c_arr() const { return cArr_.data(); }
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_string_collection_h

View File

@ -0,0 +1,186 @@
/////////////////////////////////////////////////////////////////////////////
/// @file subscribe_options.h
/// Declaration of MQTT subscribe_options class
/// @date Aug 1, 2019 @
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2019-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_subscribe_options_h
#define __mqtt_subscribe_options_h
#include "MQTTAsync.h"
#include "MQTTSubscribeOpts.h"
#include "mqtt/platform.h"
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* The MQTT v5 subscription options.
*
* The subscribe options are bitfields in the payload of a SUBSCRIBE packet,
* forming a single options byte for each topic filter in the subscription.
*
* These were added in MQTT v5. The default (zero/false) value for each
* field gives the behavior that was present in MQTT v3.1.1. To get a new
* behavior the field(s) must be set.
*
* These are defined in section 3.8.3.1 of the MQTT v5 spec.
*/
class subscribe_options
{
/** The underlying C structure */
MQTTSubscribe_options opts_;
/** The client and response have special access */
friend class async_client;
friend class response_options;
public:
/** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<subscribe_options>;
/** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const subscribe_options>;
/** Don't receive our own publications */
static constexpr bool NO_LOCAL = true;
/** Receive our own publications */
static constexpr bool LOCAL = false;
/** @deprecated Don't receive our own publications */
[[deprecated("Use NO_LOCAL")]] static constexpr bool SUBSCRIBE_NO_LOCAL = true;
/** @deprecated Receive our own publications (obsolete name) */
[[deprecated("Use LOCAL")]] static constexpr bool SUBSCRIBE_LOCAL = false;
/**
* Retain flag is only set on publications sent by a broker if in
* response to a subscribe request
*/
static constexpr bool NO_RETAIN_AS_PUBLISHED = false;
/** Keep the retain flag as on the original publish message */
static constexpr bool RETAIN_AS_PUBLISHED = true;
/** The options for subscription retain handling */
enum RetainHandling {
/** Send retained messages at the time of the subscribe */
SEND_RETAINED_ON_SUBSCRIBE = 0,
/** Send retained messages on subscribe only if subscription is new */
SEND_RETAINED_ON_NEW = 1,
/** Do not send retained messages at all */
DONT_SEND_RETAINED = 2
};
/**
* Create default subscription options.
* These are the default options corresponding to the original MQTT (v3)
* behaviors.
*/
subscribe_options() : opts_(MQTTSubscribe_options_initializer) {}
/**
* Creates a set of subscription options.
*
* @param noLocal Whether the server should send back our own
* publications, if subscribed.
* @param retainAsPublished Whether to keep the retained flag as in the
* original published message (true).
* @param retainHandling When to send retained messages:
* @li (SEND_RETAINED_ON_SUBSCRIBE, 0) At the time of the subscribe
* @li (SEND_RETAINED_ON_NEW, 1) Only at the time of a new
* subscribe
* @li (DONT_SEND_RETAINED, 2) Not at the time of subscribe
*/
explicit subscribe_options(
bool noLocal, bool retainAsPublished = false,
RetainHandling retainHandling = SEND_RETAINED_ON_SUBSCRIBE
)
: opts_(MQTTSubscribe_options_initializer) {
opts_.noLocal = noLocal ? 1 : 0;
opts_.retainAsPublished = retainAsPublished ? 1 : 0;
opts_.retainHandling = (unsigned char)retainHandling;
}
/**
* Creates the set of subscribe options from an underlying C struct.
* @param opts The Paho C subscribe options
*/
explicit subscribe_options(MQTTSubscribe_options opts) : opts_{opts} {}
#if defined(UNIT_TESTS)
/**
* Expose the underlying C struct for the unit tests.
*/
const auto& c_struct() const { return opts_; }
#endif
/**
* Gets the value of the "no local" flag.
* @return Whether the server should send back our own publications, if
* subscribed.
*/
bool get_no_local() const { return to_bool(opts_.noLocal); }
/**
* Sets the "no local" flag on or off.
* @param on Whether the server should send back our own publications,
* if subscribed.
*/
void set_no_local(bool on = true) { opts_.noLocal = on ? 1 : 0; }
/**
* Gets the "retain as published" flag.
* @return Whether to keep the retained flag as in the original
* published message.
*/
bool get_retain_as_published() const { return to_bool(opts_.retainAsPublished); }
/**
* Sets the "retain as published" flag on or off.
* @param on Whether to keep the retained flag as in the original
* published message.
*/
void set_retain_as_published(bool on = true) { opts_.retainAsPublished = on ? 1 : 0; }
/**
* Gets the "retain handling" option.
* @return When to send retained messages:
* @li (SEND_RETAINED_ON_SUBSCRIBE, 0) At the time of the subscribe
* @li (SEND_RETAINED_ON_NEW, 1) Only at the time of a new
* subscribe
* @li (DONT_SEND_RETAINED, 2) Not at the time of subscribe
*/
auto get_retain_handling() const -> RetainHandling {
return RetainHandling(opts_.retainHandling);
}
/**
* Sets the "retain handling" option.
* @param retainHandling When to send retained messages:
* @li (SEND_RETAINED_ON_SUBSCRIBE, 0) At the time of the subscribe
* @li (SEND_RETAINED_ON_NEW, 1) Only at the time of a new
* subscribe
* @li (DONT_SEND_RETAINED, 2) Not at the time of subscribe
*/
void set_retain_handling(RetainHandling retainHandling) {
opts_.retainHandling = (unsigned char)retainHandling;
}
};
/** Smart/shared pointer to a subscribe options object. */
using subscribe_options_ptr = subscribe_options::ptr_t;
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_subscribe_options_h

405
include/mqtt/thread_queue.h Normal file
View File

@ -0,0 +1,405 @@
/////////////////////////////////////////////////////////////////////////////
/// @file thread_queue.h
/// Implementation of the template class 'thread_queue', a thread-safe,
/// blocking queue for passing data between threads, safe for use with smart
/// pointers.
/// @date 09-Jan-2017
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2017-2022 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_thread_queue_h
#define __mqtt_thread_queue_h
#include <algorithm>
#include <condition_variable>
#include <deque>
#include <limits>
#include <mutex>
#include <queue>
#include <thread>
namespace mqtt {
/**
* Exception that is thrown when operations are performed on a closed
* queue.
*/
class queue_closed : public std::runtime_error
{
public:
queue_closed() : std::runtime_error("queue is closed") {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* A thread-safe queue for inter-thread communication.
*
* This is a locking queue with blocking operations. The get() operations
* can always block on an empty queue, but have variations for non-blocking
* (try_get) and bounded-time blocking (try_get_for, try_get_until).
* @par
* The default queue has a capacity that is unbounded in the practical
* sense, limited by available memory. In this mode the object will not
* block when placing values into the queue. A capacity can bet set with the
* constructor or, at any time later by calling the @ref capacity(size_type)
* method. Using this latter method, the capacity can be set to an amount
* smaller than the current size of the queue. In that case all put's to the
* queue will block until the number of items are removed from the queue to
* bring the size below the new capacity.
* @par
* The queue can be closed. After that, no new items can be placed into it;
* a `put()` calls will fail. Receivers can still continue to get any items
* out of the queue that were added before it was closed. Once there are no
* more items left in the queue after it is closed, it is considered "done".
* Nothing useful can be done with the queue.
* @par
* Note that the queue uses move semantics to place items into the queue and
* remove items from the queue. This means that the type, T, of the data
* held by the queue only needs to follow move semantics; not copy
* semantics. In addition, this means that copies of the value will @em not
* be left in the queue. This is especially useful when creating queues of
* shared pointers, as the "dead" part of the queue will not hold onto a
* reference count after the item has been removed from the queue.
*
* @tparam T The type of the items to be held in the queue.
* @tparam Container The type of the underlying container to use. It must
* support back(), front(), push_back(), pop_front().
*/
template <typename T, class Container = std::deque<T>>
class thread_queue
{
public:
/** The underlying container type to use for the queue. */
using container_type = Container;
/** The type of items to be held in the queue. */
using value_type = T;
/** The type used to specify number of items in the container. */
using size_type = typename Container::size_type;
/** The maximum capacity of the queue. */
static constexpr size_type MAX_CAPACITY = std::numeric_limits<size_type>::max();
private:
/** Object lock */
mutable std::mutex lock_;
/** Condition get signaled when item added to empty queue */
std::condition_variable notEmptyCond_;
/** Condition gets signaled then item removed from full queue */
std::condition_variable notFullCond_;
/** The capacity of the queue */
size_type cap_{MAX_CAPACITY};
/** Whether the queue is closed */
bool closed_{false};
/** The actual STL container to hold data */
std::queue<T, Container> que_;
/** Simple, scope-based lock guard */
using guard = std::lock_guard<std::mutex>;
/** General purpose guard */
using unique_guard = std::unique_lock<std::mutex>;
/** Checks if the queue is done (unsafe) */
bool is_done() const { return closed_ && que_.empty(); }
public:
/**
* Constructs a queue with the maximum capacity.
* This is effectively an unbounded queue.
*/
thread_queue() {}
/**
* Constructs a queue with the specified capacity.
* This is a bounded queue.
* @param cap The maximum number of items that can be placed in the
* queue. The minimum capacity is 1.
*/
explicit thread_queue(size_t cap) : cap_(std::max<size_type>(cap, 1)) {}
/**
* Determine if the queue is empty.
* @return @em true if there are no elements in the queue, @em false if
* there are any items in the queue.
*/
bool empty() const {
guard g{lock_};
return que_.empty();
}
/**
* Gets the capacity of the queue.
* @return The maximum number of elements before the queue is full.
*/
size_type capacity() const {
guard g{lock_};
return cap_;
}
/**
* Sets the capacity of the queue.
* Note that the capacity can be set to a value smaller than the current
* size of the queue. In that event, all calls to put() will block until
* a sufficient number
*/
void capacity(size_type cap) {
guard g{lock_};
cap_ = cap;
}
/**
* Gets the number of items in the queue.
* @return The number of items in the queue.
*/
size_type size() const {
guard g{lock_};
return que_.size();
}
/**
* Close the queue.
* Once closed, the queue will not accept any new items, but receievers
* will still be able to get any remaining items out of the queue until
* it is empty.
*/
void close() {
guard g{lock_};
closed_ = true;
notFullCond_.notify_all();
notEmptyCond_.notify_all();
}
/**
* Determines if the queue is closed.
* Once closed, the queue will not accept any new items, but receievers
* will still be able to get any remaining items out of the queue until
* it is empty.
* @return @em true if the queue is closed, @false otherwise.
*/
bool closed() const {
guard g{lock_};
return closed_;
}
/**
* Determines if all possible operations are done on the queue.
* If the queue is closed and empty, then no further useful operations
* can be done on it.
* @return @true if the queue is closed and empty, @em false otherwise.
*/
bool done() const {
guard g{lock_};
return is_done();
}
/**
* Clear the contents of the queue.
* This discards all items in the queue.
*/
void clear() {
guard g{lock_};
while (!que_.empty()) que_.pop();
notFullCond_.notify_all();
}
/**
* Put an item into the queue.
* If the queue is full, this will block the caller until items are
* removed bringing the size less than the capacity.
* @param val The value to add to the queue.
*/
void put(value_type val) {
unique_guard g{lock_};
notFullCond_.wait(g, [this] { return que_.size() < cap_ || closed_; });
if (closed_)
throw queue_closed{};
que_.emplace(std::move(val));
notEmptyCond_.notify_one();
}
/**
* Non-blocking attempt to place an item into the queue.
* @param val The value to add to the queue.
* @return @em true if the item was added to the queue, @em false if the
* item was not added because the queue is currently full.
*/
bool try_put(value_type val) {
guard g{lock_};
if (que_.size() >= cap_ || closed_)
return false;
que_.emplace(std::move(val));
notEmptyCond_.notify_one();
return true;
}
/**
* Attempt to place an item in the queue with a bounded wait.
* This will attempt to place the value in the queue, but if it is full,
* it will wait up to the specified time duration before timing out.
* @param val The value to add to the queue.
* @param relTime The amount of time to wait until timing out.
* @return @em true if the value was added to the queue, @em false if a
* timeout occurred.
*/
template <typename Rep, class Period>
bool try_put_for(value_type val, const std::chrono::duration<Rep, Period>& relTime) {
unique_guard g{lock_};
bool to = !notFullCond_.wait_for(g, relTime, [this] {
return que_.size() < cap_ || closed_;
});
if (to || closed_)
return false;
que_.emplace(std::move(val));
notEmptyCond_.notify_one();
return true;
}
/**
* Attempt to place an item in the queue with a bounded wait to an
* absolute time point.
* This will attempt to place the value in the queue, but if it is full,
* it will wait up until the specified time before timing out.
* @param val The value to add to the queue.
* @param absTime The absolute time to wait to before timing out.
* @return @em true if the value was added to the queue, @em false if a
* timeout occurred.
*/
template <class Clock, class Duration>
bool try_put_until(
value_type val, const std::chrono::time_point<Clock, Duration>& absTime
) {
unique_guard g{lock_};
bool to = !notFullCond_.wait_until(g, absTime, [this] {
return que_.size() < cap_ || closed_;
});
if (to || closed_)
return false;
que_.emplace(std::move(val));
notEmptyCond_.notify_one();
return true;
}
/**
* Retrieve a value from the queue.
* If the queue is empty, this will block indefinitely until a value is
* added to the queue by another thread,
* @param val Pointer to a variable to receive the value.
*/
bool get(value_type* val) {
if (!val)
return false;
unique_guard g{lock_};
notEmptyCond_.wait(g, [this] { return !que_.empty() || closed_; });
if (que_.empty()) // We must be done
return false;
*val = std::move(que_.front());
que_.pop();
notFullCond_.notify_one();
return true;
}
/**
* Retrieve a value from the queue.
* If the queue is empty, this will block indefinitely until a value is
* added to the queue by another thread,
* @return The value removed from the queue
*/
value_type get() {
unique_guard g{lock_};
notEmptyCond_.wait(g, [this] { return !que_.empty() || closed_; });
if (que_.empty()) // We must be done
throw queue_closed{};
value_type val = std::move(que_.front());
que_.pop();
notFullCond_.notify_one();
return val;
}
/**
* Attempts to remove a value from the queue without blocking.
* If the queue is currently empty, this will return immediately with a
* failure, otherwise it will get the next value and return it.
* @param val Pointer to a variable to receive the value.
* @return @em true if a value was removed from the queue, @em false if
* the queue is empty.
*/
bool try_get(value_type* val) {
if (!val)
return false;
guard g{lock_};
if (que_.empty())
return false;
*val = std::move(que_.front());
que_.pop();
notFullCond_.notify_one();
return true;
}
/**
* Attempt to remove an item from the queue for a bounded amount of time.
* This will retrieve the next item from the queue. If the queue is
* empty, it will wait the specified amount of time for an item to arrive
* before timing out.
* @param val Pointer to a variable to receive the value.
* @param relTime The amount of time to wait until timing out.
* @return @em true if the value was removed the queue, @em false if a
* timeout occurred.
*/
template <typename Rep, class Period>
bool try_get_for(value_type* val, const std::chrono::duration<Rep, Period>& relTime) {
if (!val)
return false;
unique_guard g{lock_};
notEmptyCond_.wait_for(g, relTime, [this] { return !que_.empty() || closed_; });
if (que_.empty())
return false;
*val = std::move(que_.front());
que_.pop();
notFullCond_.notify_one();
return true;
}
/**
* Attempt to remove an item from the queue for a bounded amount of time.
* This will retrieve the next item from the queue. If the queue is
* empty, it will wait until the specified time for an item to arrive
* before timing out.
* @param val Pointer to a variable to receive the value.
* @param absTime The absolute time to wait to before timing out.
* @return @em true if the value was removed from the queue, @em false
* if a timeout occurred.
*/
template <class Clock, class Duration>
bool try_get_until(
value_type* val, const std::chrono::time_point<Clock, Duration>& absTime
) {
if (!val)
return false;
unique_guard g{lock_};
notEmptyCond_.wait_until(g, absTime, [this] { return !que_.empty() || closed_; });
if (que_.empty())
return false;
*val = std::move(que_.front());
que_.pop();
notFullCond_.notify_one();
return true;
}
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_thread_queue_h

521
include/mqtt/token.h Normal file
View File

@ -0,0 +1,521 @@
/////////////////////////////////////////////////////////////////////////////
/// @file token.h
/// Declaration of MQTT token class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2019 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
* Frank Pagliughi - MQTT v5 support & server responses
*******************************************************************************/
#ifndef __mqtt_token_h
#define __mqtt_token_h
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>
#include "MQTTAsync.h"
#include "mqtt/buffer_ref.h"
#include "mqtt/exception.h"
#include "mqtt/iaction_listener.h"
#include "mqtt/properties.h"
#include "mqtt/server_response.h"
#include "mqtt/string_collection.h"
#include "mqtt/types.h"
namespace mqtt {
class iasync_client;
/////////////////////////////////////////////////////////////////////////////
/**
* Provides a mechanism for tracking the completion of an asynchronous
* action.
*/
class token
{
public:
/** Smart/shared pointer to an object of this class */
using ptr_t = std::shared_ptr<token>;
/** Smart/shared pointer to an object of this class */
using const_ptr_t = std::shared_ptr<const token>;
/** Weak pointer to an object of this class */
using weak_ptr_t = std::weak_ptr<token>;
/** The type of request that the token is tracking */
enum Type { CONNECT, SUBSCRIBE, PUBLISH, UNSUBSCRIBE, DISCONNECT };
private:
/** Lock guard type for this class. */
using guard = std::lock_guard<std::mutex>;
/** Unique type for this class. */
using unique_lock = std::unique_lock<std::mutex>;
/** Object monitor mutex. */
mutable std::mutex lock_;
/** Condition variable signals when the action completes */
mutable std::condition_variable cond_;
/** The type of request that the token is tracking */
Type type_;
/** The MQTT client that is processing this action */
iasync_client* cli_;
/** The action success/failure code */
int rc_{0};
/** MQTT v5 reason code */
ReasonCode reasonCode_{ReasonCode::SUCCESS};
/** Error message from the C lib (if any) */
string errMsg_;
/** The underlying C token. Note that this is just an integer */
MQTTAsync_token msgId_;
/** The topic string(s) for the action being tracked by this token */
const_string_collection_ptr topics_;
/** User supplied context */
void* userContext_;
/**
* User supplied listener.
* Note that the user listener fires after the action is marked
* complete, but before the token is signaled.
*/
iaction_listener* listener_;
/** The number of expected responses */
size_t nExpected_;
/** Whether the action has yet to complete */
bool complete_;
/** Connection response (null if not available) */
std::unique_ptr<connect_response> connRsp_;
/** Subscribe response (null if not available) */
std::unique_ptr<subscribe_response> subRsp_;
/** Unsubscribe response (null if not available) */
std::unique_ptr<unsubscribe_response> unsubRsp_;
/** Client and token-related options have special access */
friend class async_client;
friend class mock_async_client;
friend class connect_options;
friend class response_options;
friend class delivery_response_options;
friend class disconnect_options;
/**
* Resets the token back to a non-signaled state.
*/
void reset();
/**
* Sets the ID for the message.
* This is a guaranteed atomic operation.
* @param msgId The ID of the message.
*/
void set_message_id(MQTTAsync_token msgId) {
guard g(lock_);
msgId_ = msgId;
}
/**
* C-style callback for success.
* This simply passes the call on to the proper token object for
* processing.
* @param tokObj The token object to process the call. Note that this is
* @em not the user-supplied context pointer. That is
* kept in the object itself.
* @param rsp The success response.
*/
static void on_success(void* tokObj, MQTTAsync_successData* rsp);
static void on_success5(void* tokObj, MQTTAsync_successData5* rsp);
/**
* C-style callback for failure.
* This simply passes the call on to the proper token object for
* processing.
* @param tokObj The token object to process the call. Note that this is
* @em not the user-supplied context pointer. That is
* kept in the object itself.
* @param rsp The failure response.
*/
static void on_failure(void* tokObj, MQTTAsync_failureData* rsp);
static void on_failure5(void* tokObj, MQTTAsync_failureData5* rsp);
/**
* C-style callback for client (re)connection.
* This is normally only used to process a reconnect completion message.
* The initial connect() is processed via on_success/failure.
* @param tokObj Pointer to the token object used to process the call.
*/
static void on_connected(void* tokObj, char* /*cause*/);
/**
* Internal handler for the success callback.
* @param rsp The success response.
*/
void on_success(MQTTAsync_successData* rsp);
void on_success5(MQTTAsync_successData5* rsp);
/**
* Internal handler for the failure callback.
* @param rsp The failure response.
*/
void on_failure(MQTTAsync_failureData* rsp);
void on_failure5(MQTTAsync_failureData5* rsp);
/**
* Check the current return code and throw an exception if it is not a
* success code.
*/
void check_ret() const {
if (rc_ != MQTTASYNC_SUCCESS || reasonCode_ >= 0x80)
throw exception(rc_, reasonCode_, errMsg_);
}
public:
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
*/
token(Type typ, iasync_client& cli) : token{typ, cli, MQTTAsync_token(0)} {}
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when subscribe has
* completed
*/
token(Type typ, iasync_client& cli, void* userContext, iaction_listener& cb)
: token{typ, cli, const_string_collection_ptr(), userContext, cb} {}
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param topic The topic associated with the token
*/
token(Type typ, iasync_client& cli, const string& topic)
: token{typ, cli, string_collection::create(topic)} {}
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param topic The topic associated with the token
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when subscribe has
* completed
*/
token(
Type typ, iasync_client& cli, const string& topic, void* userContext,
iaction_listener& cb
)
: token{typ, cli, string_collection::create(topic), userContext, cb} {}
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param topics The topics associated with the token
*/
token(Type typ, iasync_client& cli, const_string_collection_ptr topics);
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param topics The topics associated with the token
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when subscribe has
* completed
*/
token(
Type typ, iasync_client& cli, const_string_collection_ptr topics, void* userContext,
iaction_listener& cb
);
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param tok The message ID
*/
token(Type typ, iasync_client& cli, MQTTAsync_token tok);
/**
* Virtual destructor.
*/
virtual ~token() {}
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @return A smart/shared pointer to a token.
*/
static ptr_t create(Type typ, iasync_client& cli) {
return std::make_shared<token>(typ, cli);
}
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when subscribe has
* completed
*/
static ptr_t create(
Type typ, iasync_client& cli, void* userContext, iaction_listener& cb
) {
return std::make_shared<token>(typ, cli, userContext, cb);
}
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param topic The topic associated with the token
*/
static ptr_t create(Type typ, iasync_client& cli, const string& topic) {
return std::make_shared<token>(typ, cli, topic);
}
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param topic The topic associated with the token
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when subscribe has
* completed
*/
static ptr_t create(
Type typ, iasync_client& cli, const string& topic, void* userContext,
iaction_listener& cb
) {
return std::make_shared<token>(typ, cli, topic, userContext, cb);
}
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param topics The topics associated with the token
*/
static ptr_t create(Type typ, iasync_client& cli, const_string_collection_ptr topics) {
return std::make_shared<token>(typ, cli, topics);
}
/**
* Constructs a token object.
* @param typ The type of request that the token is tracking.
* @param cli The client that created the token.
* @param topics The topics associated with the token
*
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
* @param cb callback listener that will be notified when subscribe has
*/
static ptr_t create(
Type typ, iasync_client& cli, const_string_collection_ptr topics, void* userContext,
iaction_listener& cb
) {
return std::make_shared<token>(typ, cli, topics, userContext, cb);
}
/**
* Gets the type of request the token is tracking, like CONNECT,
* PUBLISH, etc.
* @return The type of request that the token is tracking.
*/
Type get_type() const { return type_; }
/**
* Gets the action listener for this token.
* @return The action listener for this token.
*/
virtual iaction_listener* get_action_callback() const {
guard g(lock_);
return listener_;
}
/**
* Returns the MQTT client that is responsible for processing the
* asynchronous action.
* @return The client to which this token is connected.
*/
virtual iasync_client* get_client() const { return cli_; }
/**
* Returns the ID of the message that is associated with the token.
* @return The message ID of the transaction being tracked.
*/
virtual int get_message_id() const {
static_assert(sizeof(msgId_) <= sizeof(int), "MQTTAsync_token must fit into int");
return int(msgId_);
}
/**
* Gets the topic string(s) for the action being tracked by this
* token.
* @return A const pointer to the collection of topics being tracked by
* the token.
*/
virtual const_string_collection_ptr get_topics() const { return topics_; }
/**
* Retrieve the context associated with an action.
* @return The context associated with an action.
*/
virtual void* get_user_context() const {
guard g(lock_);
return userContext_;
}
/**
* Returns whether or not the action has finished.
* @return @em true if the transaction has completed, @em false if not.
*/
virtual bool is_complete() const { return complete_; }
/**
* Determines if the reference is valid.
* If the reference is invalid then it is not safe to call @em any
* member functions other than @ref is_null() and @ref empty()
* @return @em true if referring to a valid buffer, @em false if the
* reference (pointer) is null.
*/
explicit operator bool() const {
guard g(lock_);
return rc_ == MQTTASYNC_SUCCESS && reasonCode_ < 0x80;
}
/**
* Gets the return code from the action.
* This is only valid after the action has completed (i.e. if @ref
* is_complete() returns @em true).
* @return The return code from the action.
*/
virtual int get_return_code() const { return rc_; }
/**
* Register a listener to be notified when an action completes.
* @param listener The callback to be notified when actions complete.
*/
virtual void set_action_callback(iaction_listener& listener);
/**
* Store some context associated with an action.
* @param userContext optional object used to pass context to the
* callback. Use @em nullptr if not required.
*/
virtual void set_user_context(void* userContext) {
guard g(lock_);
userContext_ = userContext;
}
/**
* Sets the number of results expected.
* This is only required for subscribe many() with < MQTTv5
* @param n The number of results expected.
*/
void set_num_expected(size_t n) { nExpected_ = n; }
/**
* Gets the reason code for the operation.
* @return The reason code for the operation.
*/
ReasonCode get_reason_code() const { return reasonCode_; }
/**
* Get the error message from the C library
* @return Error message for the operation
*/
string get_error_message() const { return errMsg_; }
/**
* Blocks the current thread until the action this token is associated
* with has completed.
*/
virtual void wait();
/**
* Non-blocking check to see if the action has completed.
* @return @em true if the wait finished successfully, @em false if the
* action has not completed yet.
*/
virtual bool try_wait() {
guard g(lock_);
if (complete_)
check_ret();
return complete_;
}
/**
* Blocks the current thread until the action this token is associated
* with has completed.
* @param timeout The timeout (in milliseconds)
* @return @em true if the wait finished successfully, @em false if a
* timeout occurred.
*/
virtual bool wait_for(long timeout) {
return wait_for(std::chrono::milliseconds(timeout));
}
/**
* Waits a relative amount of time for the action to complete.
* @param relTime The amount of time to wait for the event.
* @return @em true if the event gets signaled in the specified time,
* @em false on a timeout.
*/
template <class Rep, class Period>
bool wait_for(const std::chrono::duration<Rep, Period>& relTime) {
unique_lock g(lock_);
if (!cond_.wait_for(g, std::chrono::milliseconds(relTime), [this] {
return complete_;
}))
return false;
check_ret();
return true;
}
/**
* Waits until an absolute time for the action to complete.
* @param absTime The absolute time to wait for the event.
* @return @em true if the event gets signaled in the specified time,
* @em false on a timeout.
*/
template <class Clock, class Duration>
bool wait_until(const std::chrono::time_point<Clock, Duration>& absTime) {
unique_lock g(lock_);
if (!cond_.wait_until(g, absTime, [this] { return complete_; }))
return false;
check_ret();
return true;
}
/**
* Gets the response from a connect operation.
* This returns the result of the completed operation. If the
* operation is not yet complete this will block until the result
* is available.
* @return The result of the operation.
*/
connect_response get_connect_response() const;
/**
* Gets the response from a connect operation.
* This returns the result of the completed operation. If the
* operation is not yet complete this will block until the result
* is available.
* @return The result of the operation.
*/
subscribe_response get_subscribe_response() const;
/**
* Gets the response from a connect operation.
* This returns the result of the completed operation. If the
* operation is not yet complete this will block until the result
* is available.
* @return The result of the operation.
*/
unsubscribe_response get_unsubscribe_response() const;
};
/** Smart/shared pointer to a token object */
using token_ptr = token::ptr_t;
/** Smart/shared pointer to a const token object */
using const_token_ptr = token::const_ptr_t;
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_token_h

254
include/mqtt/topic.h Normal file
View File

@ -0,0 +1,254 @@
/////////////////////////////////////////////////////////////////////////////
/// @file topic.h
/// Declaration of MQTT topic class
/// @date May 1, 2013
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_topic_h
#define __mqtt_topic_h
#include <vector>
#include "MQTTAsync.h"
#include "mqtt/delivery_token.h"
#include "mqtt/message.h"
#include "mqtt/subscribe_options.h"
#include "mqtt/types.h"
namespace mqtt {
class iasync_client;
/////////////////////////////////////////////////////////////////////////////
/**
* Represents a topic destination, used for publish/subscribe messaging.
*/
class topic
{
/** The client to which this topic is connected */
iasync_client& cli_;
/** The topic name */
string name_;
/** The default QoS */
int qos_;
/** The default retained flag */
bool retained_;
public:
/** A smart/shared pointer to this class. */
using ptr_t = std::shared_ptr<topic>;
/** A smart/shared pointer to this class. */
using const_ptr_t = std::shared_ptr<const topic>;
/**
* Construct an MQTT topic destination for messages.
* @param cli Client to which the topic is attached
* @param name The topic string
* @param qos The default QoS for publishing.
* @param retained The default retained flag for the topic.
*/
topic(
iasync_client& cli, const string& name, int qos = message::DFLT_QOS,
bool retained = message::DFLT_RETAINED
)
: cli_(cli), name_(name), qos_(qos), retained_(retained) {}
/**
* Creates a new topic
* @param cli Client to which the topic is attached
* @param name The topic string
* @param qos The default QoS for publishing.
* @param retained The default retained flag for the topic.
* @return A shared pointer to the topic.
*/
static ptr_t create(
iasync_client& cli, const string& name, int qos = message::DFLT_QOS,
bool retained = message::DFLT_RETAINED
) {
return std::make_shared<topic>(cli, name, qos, retained);
}
/**
* Gets a reference to the MQTT client used by this topic
* @return The MQTT client used by this topic
*/
iasync_client& get_client() { return cli_; }
/**
* Gets the name of the topic.
* @return The name of the topic.
*/
const string& get_name() const { return name_; }
/**
* Splits a topic string into individual fields.
*
* @param topic A slash-delimited MQTT topic string.
* @return A vector containing the fields of the topic.
*/
static std::vector<std::string> split(const std::string& topic);
/**
* Gets the default quality of service for this topic.
* @return The default quality of service for this topic.
*/
int get_qos() const { return qos_; }
/**
* Gets the default retained flag used for this topic.
* @return The default retained flag used for this topic.
*/
bool get_retained() const { return retained_; }
/**
* Sets the default quality of service for this topic.
* @param qos The default quality of service for this topic.
*/
void set_qos(int qos) {
message::validate_qos(qos);
qos_ = qos;
}
/**
* Sets the default retained flag used for this topic.
* @param retained The default retained flag used for this topic.
*/
void set_retained(bool retained) { retained_ = retained; }
/**
* Publishes a message on the topic using the default QoS and retained
* flag.
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
* @return The delivery token used to track and wait for the publish to
* complete.
*/
delivery_token_ptr publish(const void* payload, size_t n);
/**
* Publishes a message on the topic.
* @param payload the bytes to use as the message payload
* @param n the number of bytes in the payload
* @param qos the Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained whether or not this message should be retained by the
* server.
* @return The delivery token used to track and wait for the publish to
* complete.
*/
delivery_token_ptr publish(const void* payload, size_t n, int qos, bool retained);
/**
* Publishes a message on the topic using the default QoS and retained
* flag.
* @param payload the bytes to use as the message payload
* @return The delivery token used to track and wait for the publish to
* complete.
*/
delivery_token_ptr publish(binary_ref payload);
/**
* Publishes a message on the topic.
* @param payload the bytes to use as the message payload
* @param qos the Quality of Service to deliver the message at. Valid
* values are 0, 1 or 2.
* @param retained whether or not this message should be retained by the
* server.
* @return The delivery token used to track and wait for the publish to
* complete.
*/
delivery_token_ptr publish(binary_ref payload, int qos, bool retained);
/**
* Subscribe to the topic.
* @return A token used to track the progress of the operation.
*/
token_ptr subscribe(const subscribe_options& opts = subscribe_options());
/**
* Returns a string representation of this topic.
* @return The name of the topic
*/
string to_string() const { return name_; }
};
/** A smart/shared pointer to a topic object. */
using topic_ptr = topic::ptr_t;
/** A smart/shared pointer to a const topic object. */
using const_topic_ptr = topic::const_ptr_t;
/////////////////////////////////////////////////////////////////////////////
// Topic Filter
/////////////////////////////////////////////////////////////////////////////
/**
* An MQTT topic filter.
*
* This is a multi-field string, delimited by forward slashes, '/', in which
* fields can contain the wildcards:
*
* '+' - Matches a single field
* '#' - Matches all subsequent fields (must be last field in filter)
*
* It can be used to match against specific topics.
*/
class topic_filter
{
/** We store the filter as a vector of the individual fields. */
std::vector<string> fields_;
public:
/**
* Creates a new topic filter.
*
* @param filter A string MQTT topic filter. This is a slash ('/')
* delimited topic string that can contain wildcards
* '+' and '#'.
*/
explicit topic_filter(const string& filter);
/**
* Determines if the character is a wildcard, '+' or '#'
* @param c The character to check
* @return @em true if `c` is a wildcard, '+' or '#'
*/
static bool is_wildcard(char c) { return c == '+' || c == '#'; }
/**
* Determines if the string (field) is a wildcard, "+" or "#"
* @param s The string to check
* @return @em true if `c` is a wildcard, "+" or "#"
*/
static bool is_wildcard(const string& s) { return s.size() == 1 && is_wildcard(s[0]); }
/**
* Determines if the specified topic/filter contains any wildcards.
*
* @param filter The topic/filter string to check for wildcards.
* @return @em true if any of the fields contain a wildcard, @em false
* if not.
*/
static bool has_wildcards(const string& filter);
/**
* Determines if this topic filter contains any wildcards.
*
* @return @em true if any of the fields contain a wildcard, @em false
* if not.
*/
bool has_wildcards() const;
/**
* Determine if the topic matches this filter.
*
* @param topic An MQTT topic. It should not contain wildcards.
* @return @em true of the topic matches this filter, @em false
* otherwise.
*/
bool matches(const string& topic) const;
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_topic_h

View File

@ -0,0 +1,622 @@
/////////////////////////////////////////////////////////////////////////////
/// @file topic_matcher.h
/// Declaration of MQTT topic_matcher class
/// @date April 23, 2022
/// @author Frank Pagliughi
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2022-2025 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#ifndef __mqtt_topic_matcher_h
#define __mqtt_topic_matcher_h
#include <forward_list>
#include <initializer_list>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "mqtt/topic.h"
#include "mqtt/types.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
/**
* A collection of MQTT topic filters mapped to arbitrary values.
*
* This can be used to get an iterator to all filters in the collection that
* match a topic. A typical use case might be to match incoming messages to
* specific callback functions based on topics, such as
*
* To test against a single filter, see
* [`TopicFilter`](crate::TopicFilter). This collection is more commonly
* used when there are a number of filters and each needs to be associated
* with a particular action or piece of data. Note, however, that a single
* incoming topic could match against several items in the collection. For
* example, the topic:
*
* @code
* data/temperature/engine
* @endcode
*
* Could match against the filters:
* @code
* data/temperature/engine
* data/temperature/#
* data/+/engine
* @endcode
*
* Thus, the collection gives an iterator for the items matching a topic.
*
* A common use for this would be to store callbacks to process incoming
* messages based on topics.
*
* This code was adapted from the Eclipse Python `MQTTMatcher` class:
*
*<https://github.com/eclipse/paho.mqtt.python/blob/master/src/paho/mqtt/matcher.py>
*
* which use a prefix tree (trie) to store the values.
*
* For example, if you had a `topic_mapper<int>` and you inserted:
* @code
* topic_matcher<int> tm{
* {"some/random/topic", 42},
* {"some/#", 99},
* {"some/+/topic", 33}
* };
* @endcode
*
* The collection would be built like:
* @code
* "some" -> <null>
* "random" -> <null>
* "topic" -> <42>
* "#" -> <99>
* "+" -> <null>
* "topic" -> <33>
* @endcode
*
* Note that the collection has two types of iterators. The basic `iterator`
* is a normal C++ iterator over *all* the items in the collection. It will
* visit every node in the collection and produce all items. This is not the
* typical use case for the collection, but can be used for diagnostics,
* etc, to show the full contents of the collection.
*
* The more common use case is the `match_iterator`, returned by the
* `topic_matcher::matches(string)` method. This is an optimized search
* iterator for finding all the filters and values that match the specified
* topic string.
*/
template <typename T>
class topic_matcher
{
public:
using key_type = string;
using mapped_type = T;
using value_type = std::pair<key_type, mapped_type>;
using reference = value_type;
using const_reference = const value_type&;
using value_ptr = std::unique_ptr<value_type>;
using mapped_ptr = std::unique_ptr<mapped_type>;
private:
/**
* The nodes of the collection.
*/
struct node
{
using ptr_t = std::unique_ptr<node>;
using map_t = std::map<string, ptr_t>;
/** The value that matches the topic at this node, if any */
value_ptr content;
/** Child nodes mapped by the next field of the topic */
map_t children;
/** Creates a new, empty node */
static ptr_t create() { return std::make_unique<node>(); }
/** Determines if this node is empty (no content or children) */
bool empty() const { return !content && children.empty(); }
/** Removes the empty nodes under this one. */
void prune() {
for (auto& child : children) {
child.second->prune();
}
for (auto child = children.cbegin(); child != children.cend();) {
if (child->second->empty()) {
child = children.erase(child);
}
else {
++child;
}
}
}
};
using node_ptr = typename node::ptr_t;
using node_map = typename node::map_t;
/** The root node of the collection */
node_ptr root_;
public:
/** Generic iterator over all items in the collection. */
class iterator
{
/** The last-found value */
value_type* pval_;
/** The nodes still to be checked, used as a stack */
std::vector<node*> nodes_;
void next() {
// If there are no nodes left to search, we're done.
if (nodes_.empty()) {
pval_ = nullptr;
return;
}
// Get the next node to search.
auto snode = std::move(nodes_.back());
nodes_.pop_back();
// Push the children onto the stack for later
for (auto const& child : snode->children) {
nodes_.push_back(child.second.get());
}
// If there's a value in this node, use it;
// otherwise keep looking.
pval_ = snode->content.get();
if (!pval_)
this->next();
}
friend class topic_matcher;
iterator(value_type* pval) : pval_{pval} {}
iterator(node* root) : pval_{nullptr} {
nodes_.push_back(root);
next();
}
public:
/**
* Gets a reference to the current value.
* @return A reference to the current value.
*/
reference operator*() noexcept { return *pval_; }
/**
* Gets a const reference to the current value.
* @return A const reference to the current value.
*/
const_reference operator*() const noexcept { return *pval_; }
/**
* Get a pointer to the current value.
* @return A pointer to the current value.
*/
value_type* operator->() noexcept { return pval_; }
/**
* Get a const pointer to the current value.
* @return A const pointer to the current value.
*/
const value_type* operator->() const noexcept { return pval_; }
/**
* Postfix increment operator.
* @return An iterator pointing to the previous matching item.
*/
iterator operator++(int) noexcept {
auto tmp = *this;
this->next();
return tmp;
}
/**
* Prefix increment operator.
* @return An iterator pointing to the next matching item.
*/
iterator& operator++() noexcept {
this->next();
return *this;
}
/**
* Compares two iterators to see if they don't refer to the same
* node.
*
* @param other The other iterator to compare against this one.
* @return @em true if they don't match, @em false if they do
*/
bool operator!=(const iterator& other) const noexcept { return pval_ != other.pval_; }
};
/** A const iterator over all itemsin the collection. */
class const_iterator : public iterator
{
using base = iterator;
friend class topic_matcher;
const_iterator(iterator it) : base(it) {}
public:
/**
* Gets a const reference to the current value.
* @return A const reference to the current value.
*/
const_reference operator*() const noexcept { return base::operator*(); }
/**
* Get a const pointer to the current value.
* @return A const pointer to the current value.
*/
const value_type* operator->() const noexcept { return base::operator->(); }
};
/**
* Iterator that efficiently searches the collection for topic
* matches.
*/
class match_iterator
{
/** Information about a node that needs to be searched. */
struct search_node
{
/** The current node being searched. */
node* node_;
/** The fields of the topic still to be searched. */
std::forward_list<string> fields_;
/** Whether this is the first/root node */
bool first_;
search_node(node* nd, const std::forward_list<string>& sy, bool first = false)
: node_{nd}, fields_{sy}, first_{first} {}
search_node(node* nd, std::forward_list<string>&& sy, bool first = false)
: node_{nd}, fields_{std::move(sy)}, first_{first} {}
};
/** The last-found value */
value_type* pval_;
/** The nodes still to be checked, used as a stack */
std::vector<search_node> nodes_;
/**
* Move the next iterator to the next value, or to end(), if none
* left.
*
* This will keep recursing until it finds a matching node that
* contains a value or it reaches the end.
*/
void next() {
pval_ = nullptr;
// If there are no nodes left to search, we're done.
if (nodes_.empty())
return;
// Get the next node to search.
auto snode = std::move(nodes_.back());
nodes_.pop_back();
// If we're at the end of the topic fields, we either have a value,
// or need to move on to the next node to search.
if (snode.fields_.empty()) {
pval_ = snode.node_->content.get();
if (!pval_)
this->next();
return;
}
// Get the next field of the topic to search
auto field = std::move(snode.fields_.front());
snode.fields_.pop_front();
typename node_map::iterator child;
const auto map_end = snode.node_->children.end();
// Look for an exact match
if ((child = snode.node_->children.find(field)) != map_end) {
nodes_.push_back({child->second.get(), snode.fields_});
}
// Topics starting with '$' don't match wildcards in the first field
// MQTT v5 Spec, Section 4.7.2:
// https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901246
if (!snode.first_ || field.empty() || field[0] != '$') {
// Look for a single-field wildcard match
if ((child = snode.node_->children.find("+")) != map_end) {
nodes_.push_back({child->second.get(), snode.fields_});
}
// Look for a terminating match
if ((child = snode.node_->children.find("#")) != map_end) {
// By definition, a '#' is a terminating leaf
pval_ = child->second->content.get();
return;
}
}
this->next();
}
friend class topic_matcher;
match_iterator() : pval_{nullptr} {}
match_iterator(value_type* pval) : pval_{pval} {}
match_iterator(node* root, const string& topic) : pval_{nullptr} {
auto v = topic::split(topic);
std::forward_list<string> fields{v.begin(), v.end()};
nodes_.push_back(search_node{root, std::move(fields), true});
next();
}
public:
/**
* Gets a reference to the current value.
* @return A reference to the current value.
*/
reference operator*() noexcept { return *pval_; }
/**
* Gets a const reference to the current value.
* @return A const reference to the current value.
*/
const_reference operator*() const noexcept { return *pval_; }
/**
* Get a pointer to the current value.
* @return A pointer to the current value.
*/
value_type* operator->() noexcept { return pval_; }
/**
* Get a const pointer to the current value.
* @return A const pointer to the current value.
*/
const value_type* operator->() const noexcept { return pval_; }
/**
* Postfix increment operator.
* @return An iterator pointing to the previous matching item.
*/
match_iterator operator++(int) noexcept {
auto tmp = *this;
this->next();
return tmp;
}
/**
* Prefix increment operator.
* @return An iterator pointing to the next matching item.
*/
match_iterator& operator++() noexcept {
this->next();
return *this;
}
/**
* Compares two iterators to see if they don't refer to the same
* node.
*
* @param other The other iterator to compare against this one.
* @return @em true if they don't match, @em false if they do
*/
bool operator!=(const match_iterator& other) const noexcept {
return pval_ != other.pval_;
}
};
/**
* A const match iterator.
*/
class const_match_iterator : public match_iterator
{
using base = match_iterator;
friend class topic_matcher;
const_match_iterator(match_iterator it) : base(it) {}
public:
/**
* Gets a const reference to the current value.
* @return A const reference to the current value.
*/
const_reference operator*() const noexcept { return base::operator*(); }
/**
* Get a const pointer to the current value.
* @return A const pointer to the current value.
*/
const value_type* operator->() const noexcept { return base::operator->(); }
};
/**
* Creates new, empty collection.
*/
topic_matcher() : root_(node::create()) {}
/**
* Creates a new collection with a list of key/value pairs.
*
* This can be used to create a connection from a table of entries, as
* key/value pairs, like:
*
* topic_matcher<int> matcher {
* { "#", -1 },
* { "some/random/topic", 42 },
* { "some/#", 99 }
* }
*
* @param lst The list of key/value pairs to populate the collection.
*/
topic_matcher(std::initializer_list<value_type> lst) : root_(node::create()) {
for (const auto& v : lst) {
insert(v);
}
}
/**
* Determines if the collection is empty.
* @return @em true if the collection is empty, @em false if it contains
* any filters.
*/
bool empty() const { return root_.empty(); }
/**
* Inserts a new key/value pair into the collection.
* @param val The value to place in the collection.
*/
void insert(value_type&& val) {
auto nd = root_.get();
auto fields = topic::split(val.first);
for (const auto& field : fields) {
auto it = nd->children.find(field);
if (it == nd->children.end()) {
nd->children[field] = node::create();
it = nd->children.find(field);
}
nd = it->second.get();
}
nd->content = std::make_unique<value_type>(std::move(val));
}
/**
* Inserts a new value into the collection.
* @param key The topic/filter entry
* @param val The value to associate with that entry.
*/
void insert(const value_type& val) {
value_type v{val};
this->insert(std::move(v));
}
/**
* Removes an entry from the collection.
*
* This removes the value from the internal node, but leaves the node in
* the collection, even if it is empty.
* @param filter The topic filter to remove.
* @return A unique pointer to the value, if any.
*/
mapped_ptr remove(const key_type& filter) {
auto nd = root_.get();
auto fields = topic::split(filter);
for (auto& field : fields) {
auto it = nd->children.find(field);
if (it == nd->children.end())
return mapped_ptr{};
nd = it->second.get();
}
value_ptr valpair;
nd->content.swap(valpair);
return (valpair) ? std::make_unique<mapped_type>(valpair->second) : mapped_ptr{};
}
/**
* Removes the empty nodes in the collection.
*/
void prune() { root_->prune(); }
/**
* Gets an iterator to the full collection of filters.
* @return An iterator to the full collection of filters.
*/
iterator begin() { return iterator{root_.get()}; }
/**
* Gets an iterator to the end of the collection of filters.
* @return An iterator to the end of collection of filters.
*/
iterator end() { return iterator{static_cast<value_type*>(nullptr)}; }
/**
* Gets an iterator to the end of the collection of filters.
* @return An iterator to the end of collection of filters.
*/
const_iterator end() const noexcept {
return const_iterator{static_cast<value_type*>(nullptr)};
}
/**
* Gets a const iterator to the full collection of filters.
* @return A const iterator to the full collection of filters.
*/
const_iterator cbegin() const { return const_iterator{root_.get()}; }
/**
* Gets a const iterator to the end of the collection of filters.
* @return A const iterator to the end of collection of filters.
*/
const_iterator cend() const noexcept { return end(); }
/**
* Gets a pointer to the value at the requested key.
* @param filter The topic filter entry to find.
* @return An iterator to the value if found, @em end() if not found.
*/
iterator find(const key_type& filter) {
auto nd = root_.get();
auto fields = topic::split(filter);
for (auto& field : fields) {
auto it = nd->children.find(field);
if (it == nd->children.end())
return end();
nd = it->second.get();
}
return iterator{nd->content.get()};
}
/**
* Gets a const pointer to the value at the requested key.
* @param filter The topic filter entry to find.
* @return A const pointer to the value if found, @em nullptr if not
* found.
*/
const_iterator find(const key_type& filter) const {
return const_cast<topic_matcher*>(this)->find(filter);
}
/**
* Gets an match_iterator that can find the matches to the topic.
* @param topic The topic to search for matches.
* @return An iterator that can find the matches to the topic.
*/
match_iterator matches(const string& topic) { return match_iterator(root_.get(), topic); }
/**
* Gets a const iterator that can find the matches to the topic.
* @param topic The topic to search for matches.
* @return A const iterator that can find the matches to the topic.
*/
const_match_iterator matches(const string& topic) const {
return match_iterator(root_.get(), topic);
}
/**
* Gets an iterator for the end of the collection.
*
* This simply returns an empty/null iterator which we can use to signal
* the end of the collection.
*
* @return An empty/null iterator indicating the end of the collection.
*/
const_match_iterator matches_end() const noexcept { return match_iterator{}; }
/**
* Gets an iterator for the end of the collection.
*
* This simply returns an empty/null iterator which we can use to signal
* the end of the collection.
*
* @return An empty/null iterator indicating the end of the collection.
*/
const_match_iterator matches_cend() const noexcept { return match_iterator{}; }
/**
* Determines if there are any matches for the specified topic.
* @param topic The topic to search for matches.
* @return Whether there are any matches for the topic in the
* collection.
*/
bool has_match(const string& topic) { return matches(topic) != matches_cend(); }
};
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_topic_matcher_h

View File

@ -8,11 +8,11 @@
* Copyright (c) 2015-2017 Frank Pagliughi <fpagliughi@mindspring.com> * Copyright (c) 2015-2017 Frank Pagliughi <fpagliughi@mindspring.com>
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution. * and Eclipse Distribution License v1.0 which accompany this distribution.
* *
* The Eclipse Public License is available at * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php. * http://www.eclipse.org/org/documents/edl-v10.php.
* *
@ -23,10 +23,13 @@
#ifndef __mqtt_types_h #ifndef __mqtt_types_h
#define __mqtt_types_h #define __mqtt_types_h
#include <chrono>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory>
#include <chrono> // Pull in reason codes here for backward compatability with old version
#include "mqtt/reason_code.h"
namespace mqtt { namespace mqtt {
@ -39,70 +42,13 @@ using byte = uint8_t;
/** An mqtt string is just a std::string */ /** An mqtt string is just a std::string */
using string = std::string; using string = std::string;
/** A binary blob of data is, umm, just a string too! */ /** A binary blob of data is, umm, just a string too! */
using binary = std::string; //std::basic_string<byte>; using binary = std::string;
/** Smart/shared pointer to a const string */ /** Smart/shared pointer to a const string */
using string_ptr = std::shared_ptr<const string>; using string_ptr = std::shared_ptr<const string>;
/** Smart/shared pointer to a const binary blob */ /** Smart/shared pointer to a const binary blob */
using binary_ptr = std::shared_ptr<const binary>; using binary_ptr = std::shared_ptr<const binary>;
/////////////////////////////////////////////////////////////////////////////
// General protocol enumerations
/**
* The MQTT v5 Reason Codes.
*
* These map to the Paho C MQTTReasonCodes
*/
enum ReasonCode {
SUCCESS = 0,
NORMAL_DISCONNECTION = 0,
GRANTED_QOS_0 = 0,
GRANTED_QOS_1 = 1,
GRANTED_QOS_2 = 2,
DISCONNECT_WITH_WILL_MESSAGE = 4,
NO_MATCHING_SUBSCRIBERS = 16,
NO_SUBSCRIPTION_FOUND = 17,
CONTINUE_AUTHENTICATION = 24,
RE_AUTHENTICATE = 25,
UNSPECIFIED_ERROR = 128,
MALFORMED_PACKET = 129,
PROTOCOL_ERROR = 130,
IMPLEMENTATION_SPECIFIC_ERROR = 131,
UNSUPPORTED_PROTOCOL_VERSION = 132,
CLIENT_IDENTIFIER_NOT_VALID = 133,
BAD_USER_NAME_OR_PASSWORD = 134,
NOT_AUTHORIZED = 135,
SERVER_UNAVAILABLE = 136,
SERVER_BUSY = 137,
BANNED = 138,
SERVER_SHUTTING_DOWN = 139,
BAD_AUTHENTICATION_METHOD = 140,
KEEP_ALIVE_TIMEOUT = 141,
SESSION_TAKEN_OVER = 142,
TOPIC_FILTER_INVALID = 143,
TOPIC_NAME_INVALID = 144,
PACKET_IDENTIFIER_IN_USE = 145,
PACKET_IDENTIFIER_NOT_FOUND = 146,
RECEIVE_MAXIMUM_EXCEEDED = 147,
TOPIC_ALIAS_INVALID = 148,
PACKET_TOO_LARGE = 149,
MESSAGE_RATE_TOO_HIGH = 150,
QUOTA_EXCEEDED = 151,
ADMINISTRATIVE_ACTION = 152,
PAYLOAD_FORMAT_INVALID = 153,
RETAIN_NOT_SUPPORTED = 154,
QOS_NOT_SUPPORTED = 155,
USE_ANOTHER_SERVER = 156,
SERVER_MOVED = 157,
SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 158,
CONNECTION_RATE_EXCEEDED = 159,
MAXIMUM_CONNECT_TIME = 160,
SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED = 161,
WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 162,
MQTTPP_V3_CODE = 255 // This is not a protocol code; used internally by the library
};
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// Time functions // Time functions
@ -114,7 +60,7 @@ enum ReasonCode {
*/ */
template <class Rep, class Period> template <class Rep, class Period>
std::chrono::seconds to_seconds(const std::chrono::duration<Rep, Period>& dur) { std::chrono::seconds to_seconds(const std::chrono::duration<Rep, Period>& dur) {
return std::chrono::duration_cast<std::chrono::seconds>(dur); return std::chrono::duration_cast<std::chrono::seconds>(dur);
} }
/** /**
@ -125,7 +71,7 @@ std::chrono::seconds to_seconds(const std::chrono::duration<Rep, Period>& dur) {
*/ */
template <class Rep, class Period> template <class Rep, class Period>
long to_seconds_count(const std::chrono::duration<Rep, Period>& dur) { long to_seconds_count(const std::chrono::duration<Rep, Period>& dur) {
return (long) to_seconds(dur).count(); return (long)to_seconds(dur).count();
} }
/** /**
@ -136,7 +82,7 @@ long to_seconds_count(const std::chrono::duration<Rep, Period>& dur) {
*/ */
template <class Rep, class Period> template <class Rep, class Period>
std::chrono::milliseconds to_milliseconds(const std::chrono::duration<Rep, Period>& dur) { std::chrono::milliseconds to_milliseconds(const std::chrono::duration<Rep, Period>& dur) {
return std::chrono::duration_cast<std::chrono::milliseconds>(dur); return std::chrono::duration_cast<std::chrono::milliseconds>(dur);
} }
/** /**
@ -147,7 +93,7 @@ std::chrono::milliseconds to_milliseconds(const std::chrono::duration<Rep, Perio
*/ */
template <class Rep, class Period> template <class Rep, class Period>
long to_milliseconds_count(const std::chrono::duration<Rep, Period>& dur) { long to_milliseconds_count(const std::chrono::duration<Rep, Period>& dur) {
return (long) to_milliseconds(dur).count(); return (long)to_milliseconds(dur).count();
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
@ -167,18 +113,15 @@ inline bool to_bool(int n) { return n != 0; }
inline int to_int(bool b) { return b ? (!0) : 0; } inline int to_int(bool b) { return b ? (!0) : 0; }
/** /**
* Gets a valid string for the char pointer. * Gets a valid string for the char pointer, returning an empty string if
* the pointer is NULL.
* @param cstr A C-string pointer * @param cstr A C-string pointer
* @return A string copy of the C array. If `cstr` is NULL, this returns an * @return A string copy of the C array. If `cstr` is NULL, this returns an
* empty string. * empty string.
*/ */
inline string to_string(const char* cstr) { inline string to_string(const char* cstr) { return cstr ? string(cstr) : string(); }
return cstr ? string(cstr) : string();
}
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// end namespace mqtt } // namespace mqtt
}
#endif // __mqtt_types_h
#endif // __mqtt_types_h

281
include/mqtt/will_options.h Normal file
View File

@ -0,0 +1,281 @@
/////////////////////////////////////////////////////////////////////////////
/// @file will_options.h
/// Declaration of MQTT will_options class
/// @date Jul 7, 2016
/// @author Guilherme M. Ferreira
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2016 Guilherme M. Ferreira <guilherme.maciel.ferreira@gmail.com>
* Copyright (c) 2016-2023 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Guilherme M. Ferreira - initial implementation and documentation
* Frank Pagliughi - added copy & move operations
*******************************************************************************/
#ifndef __mqtt_will_options_h
#define __mqtt_will_options_h
#include "MQTTAsync.h"
#include "mqtt/message.h"
#include "mqtt/platform.h"
#include "mqtt/topic.h"
#include "mqtt/types.h"
namespace mqtt {
class connect_options;
/////////////////////////////////////////////////////////////////////////////
/**
* Holds the set of options that govern the Last Will and Testament feature.
*
* @note
* This wraps struct v1 of the C library's MQTTAsync_willOptions structure.
* It sets the LWT binary payload, via the 'payload' struct field, and
leaves the 'message' field as a nullptr.
*/
class will_options
{
public:
/** The default QoS for the LWT, if unspecified */
static constexpr int DFLT_QOS = 0;
/** The default retained flag for LWT, if unspecified */
static constexpr bool DFLT_RETAINED = false;
private:
/** A default C struct to support re-initializing variables */
static constexpr MQTTAsync_willOptions DFLT_C_STRUCT MQTTAsync_willOptions_initializer;
/** The underlying C LWT options */
MQTTAsync_willOptions opts_{DFLT_C_STRUCT};
/** LWT message topic **/
string_ref topic_;
/** LWT message text */
binary_ref payload_;
/**
* The properties for the LWT message.
* Strangely, in the C lib, the will properties are not in the
* willOptions struct, but are rather in the connectOptions.
* So we keep the cached properties here, but need to transfer them to
* the connect_options when we're added to that struct.
*/
properties props_;
/** The connect options has special access */
friend class connect_options;
/**
* Gets a pointer to the C-language NUL-terminated strings for the
* struct.
* Some structs, such as this one, require valid pointers at all times,
* while others expect NULL pointers for missing parameters.
* So we always return a pointer to a valid C char array, even when
* empty.
* @param sr The C++ string object.
* @return Pointer to a NUL terminated string. This is only valid until
* the next time the string is updated. This is never nullptr.
*/
const char* c_str(const string_ref& sr) { return sr ? sr.to_string().c_str() : nullptr; }
public:
/** Smart/shared pointer to an object of this class. */
using ptr_t = std::shared_ptr<will_options>;
/** Smart/shared pointer to a const object of this class. */
using const_ptr_t = std::shared_ptr<const will_options>;
/** Smart/shared pointer to an object of this class. */
using unique_ptr_t = std::unique_ptr<will_options>;
/**
* Constructs a new object using the default values.
*/
will_options();
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param top The LWT message is published to the this topic.
* @param payload The message that is published to the Will Topic.
* @param payload_len The message size in bytes
* @param qos The message Quality of Service.
* @param retained Tell the broker to keep the LWT message after send to
* subscribers.
* @param props MQTT v5 properties for the will message.
*/
will_options(
string_ref top, const void* payload, size_t payload_len, int qos = DFLT_QOS,
bool retained = DFLT_RETAINED, const properties& props = properties()
);
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param top The LWT message is published to the this topic.
* @param payload The message that is published to the Will Topic.
* @param payload_len The message size in bytes.
* @param qos The message Quality of Service.
* @param retained Tell the broker to keep the LWT message after send to
* subscribers.
* @param props MQTT v5 properties for the will message.
*/
will_options(
const topic& top, const void* payload, size_t payload_len, int qos = DFLT_QOS,
bool retained = DFLT_RETAINED, const properties& props = properties()
);
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param top The LWT message is published to the this topic.
* @param payload The message payload that is published to the Will
* Topic.
* @param qos The message Quality of Service.
* @param retained Tell the broker to keep the LWT message after send to
* subscribers.
* @param props MQTT v5 properties for the will message.
*/
will_options(
string_ref top, binary_ref payload, int qos = DFLT_QOS, bool retained = DFLT_RETAINED,
const properties& props = properties()
);
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param top The LWT message is published to the this topic.
* @param payload The message payload that is published to the Will
* Topic, as a string.
* @param qos The message Quality of Service.
* @param retained Tell the broker to keep the LWT message after send to
* subscribers.
* @param props MQTT v5 properties for the will message.
*/
will_options(
string_ref top, const string& payload, int qos = DFLT_QOS, bool retained = DFLT_QOS,
const properties& props = properties()
);
/**
* Sets the "Last Will and Testament" (LWT) for the connection.
* @param msg The message that is published to the Will Topic.
*/
will_options(const message& msg);
/**
* Copy constructor for the LWT options.
* @param opt The other options.
*/
will_options(const will_options& opt);
/**
* Move constructor for the LWT options.
* @param opt The other options.
*/
will_options(will_options&& opt);
/**
* Copy assignment for the LWT options.
* @param opt The other options.
*/
will_options& operator=(const will_options& opt);
/**
* Move assignment for the LWT options.
* @param opt The other options.
*/
will_options& operator=(will_options&& opt);
/**
* Expose the underlying C struct for the unit tests.
*/
#if defined(UNIT_TESTS)
const MQTTAsync_willOptions& c_struct() const { return opts_; }
#endif
/**
* Gets the LWT message topic name.
* @return The LWT message topic name.
*/
string get_topic() const { return topic_ ? topic_.to_string() : string(); }
/**
* Gets the LWT message payload.
* @return The LWT message payload.
*/
const binary_ref& get_payload() const { return payload_; }
/**
* Gets the LWT message payload as a string.
* @return The LWT message payload as a string.
*/
string get_payload_str() const { return payload_ ? payload_.to_string() : string(); }
/**
* Gets the QoS value for the LWT message.
* @return The QoS value for the LWT message.
*/
int get_qos() const { return opts_.qos; }
/**
* Gets the 'retained' flag for the LWT message.
* @return The 'retained' flag for the LWT message.
*/
bool is_retained() const { return opts_.retained != 0; }
/**
* Gets the LWT message as a message object.
* @return A pointer to a copy of the LWT message.
*/
const_message_ptr get_message() const {
return message::create(topic_, payload_, opts_.qos, to_bool(opts_.retained));
}
/**
* Sets the LWT message topic name.
* @param top The topic where to sent the message
*/
void set_topic(string_ref top);
/**
* Sets the LWT message text.
* @param msg The LWT message
*/
void set_payload(binary_ref msg);
/**
* Sets the LWT message text.
* @param msg The LWT message
*/
void set_payload(string msg) { set_payload(binary_ref(std::move(msg))); }
/**
* Sets the QoS value.
* @param qos The LWT message QoS
*/
void set_qos(const int qos) { opts_.qos = qos; }
/**
* Sets the retained flag.
* @param retained Tell the broker to keep the LWT message after send to
* subscribers.
*/
void set_retained(bool retained) { opts_.retained = to_int(retained); }
/**
* Gets the connect properties.
* @return A const reference to the properties for the connect.
*/
const properties& get_properties() const { return props_; }
/**
* Sets the properties for the connect.
* @param props The properties to place into the message.
*/
void set_properties(const properties& props) { props_ = props; }
/**
* Moves the properties for the connect.
* @param props The properties to move into the connect object.
*/
void set_properties(properties&& props) { props_ = std::move(props); }
};
/** Shared pointer to a will options object. */
using will_options_ptr = will_options::ptr_t;
/** Shared pointer to a const will options object. */
using const_will_options_ptr = will_options::const_ptr_t;
/** Unique pointer to a will options object. */
using will_options_unique_ptr = will_options::unique_ptr_t;
/////////////////////////////////////////////////////////////////////////////
} // namespace mqtt
#endif // __mqtt_will_options_h

6
m4/.gitignore vendored
View File

@ -1,6 +0,0 @@
libtool.m4
ltoptions.m4
ltsugar.m4
ltversion.m4
lt~obsolete.m4

View File

@ -1,3 +0,0 @@
Fix the problem
aclocal: error: couldn't open directory 'm4': No such file or directory

View File

@ -1,562 +0,0 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the specified
# version of the C++ standard. If necessary, add switches to CXX and
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
# or '14' (for the C++14 standard).
#
# The second argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
# -std=c++11). If neither is specified, you get whatever works, with
# preference for an extended mode.
#
# The third argument, if specified 'mandatory' or if left unspecified,
# indicates that baseline support for the specified C++ standard is
# required and that the macro should error out if no mode with that
# support is found. If specified 'optional', then configuration proceeds
# regardless, after defining HAVE_CXX${VERSION} if and only if a
# supporting mode is found.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 4
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
dnl (serial version number 13).
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
m4_if([$1], [11], [],
[$1], [14], [],
[$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$2], [], [],
[$2], [ext], [],
[$2], [noext], [],
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
AC_LANG_PUSH([C++])dnl
ac_success=no
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
ax_cv_cxx_compile_cxx$1,
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[ax_cv_cxx_compile_cxx$1=yes],
[ax_cv_cxx_compile_cxx$1=no])])
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
ac_success=yes
fi
m4_if([$2], [noext], [], [dnl
if test x$ac_success = xno; then
for switch in -std=gnu++$1 -std=gnu++0x; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
fi])
m4_if([$2], [ext], [], [dnl
if test x$ac_success = xno; then
dnl HP's aCC needs +std=c++11 according to:
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
dnl Cray's crayCC needs "-h std=c++11"
for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
fi])
AC_LANG_POP([C++])
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
if test x$ac_success = xno; then
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
fi
fi
if test x$ac_success = xno; then
HAVE_CXX$1=0
AC_MSG_NOTICE([No compiler with C++$1 support was found])
else
HAVE_CXX$1=1
AC_DEFINE(HAVE_CXX$1,1,
[define if the compiler supports basic C++$1 syntax])
fi
AC_SUBST(HAVE_CXX$1)
])
dnl Test body for checking C++11 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
)
dnl Test body for checking C++14 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
)
dnl Tests for new features in C++11
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
// If the compiler admits that it is not ready for C++11, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
#else
namespace cxx11
{
namespace test_static_assert
{
template <typename T>
struct check
{
static_assert(sizeof(int) <= sizeof(T), "not big enough");
};
}
namespace test_final_override
{
struct Base
{
virtual void f() {}
};
struct Derived : public Base
{
virtual void f() override {}
};
}
namespace test_double_right_angle_brackets
{
template < typename T >
struct check {};
typedef check<void> single_type;
typedef check<check<void>> double_type;
typedef check<check<check<void>>> triple_type;
typedef check<check<check<check<void>>>> quadruple_type;
}
namespace test_decltype
{
int
f()
{
int a = 1;
decltype(a) b = 2;
return a + b;
}
}
namespace test_type_deduction
{
template < typename T1, typename T2 >
struct is_same
{
static const bool value = false;
};
template < typename T >
struct is_same<T, T>
{
static const bool value = true;
};
template < typename T1, typename T2 >
auto
add(T1 a1, T2 a2) -> decltype(a1 + a2)
{
return a1 + a2;
}
int
test(const int c, volatile int v)
{
static_assert(is_same<int, decltype(0)>::value == true, "");
static_assert(is_same<int, decltype(c)>::value == false, "");
static_assert(is_same<int, decltype(v)>::value == false, "");
auto ac = c;
auto av = v;
auto sumi = ac + av + 'x';
auto sumf = ac + av + 1.0;
static_assert(is_same<int, decltype(ac)>::value == true, "");
static_assert(is_same<int, decltype(av)>::value == true, "");
static_assert(is_same<int, decltype(sumi)>::value == true, "");
static_assert(is_same<int, decltype(sumf)>::value == false, "");
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
return (sumf > 0.0) ? sumi : add(c, v);
}
}
namespace test_noexcept
{
int f() { return 0; }
int g() noexcept { return 0; }
static_assert(noexcept(f()) == false, "");
static_assert(noexcept(g()) == true, "");
}
namespace test_constexpr
{
template < typename CharT >
unsigned long constexpr
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
{
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
}
template < typename CharT >
unsigned long constexpr
strlen_c(const CharT *const s) noexcept
{
return strlen_c_r(s, 0UL);
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("1") == 1UL, "");
static_assert(strlen_c("example") == 7UL, "");
static_assert(strlen_c("another\0example") == 7UL, "");
}
namespace test_rvalue_references
{
template < int N >
struct answer
{
static constexpr int value = N;
};
answer<1> f(int&) { return answer<1>(); }
answer<2> f(const int&) { return answer<2>(); }
answer<3> f(int&&) { return answer<3>(); }
void
test()
{
int i = 0;
const int c = 0;
static_assert(decltype(f(i))::value == 1, "");
static_assert(decltype(f(c))::value == 2, "");
static_assert(decltype(f(0))::value == 3, "");
}
}
namespace test_uniform_initialization
{
struct test
{
static const int zero {};
static const int one {1};
};
static_assert(test::zero == 0, "");
static_assert(test::one == 1, "");
}
namespace test_lambdas
{
void
test1()
{
auto lambda1 = [](){};
auto lambda2 = lambda1;
lambda1();
lambda2();
}
int
test2()
{
auto a = [](int i, int j){ return i + j; }(1, 2);
auto b = []() -> int { return '0'; }();
auto c = [=](){ return a + b; }();
auto d = [&](){ return c; }();
auto e = [a, &b](int x) mutable {
const auto identity = [](int y){ return y; };
for (auto i = 0; i < a; ++i)
a += b--;
return x + identity(a + b);
}(0);
return a + b + c + d + e;
}
int
test3()
{
const auto nullary = [](){ return 0; };
const auto unary = [](int x){ return x; };
using nullary_t = decltype(nullary);
using unary_t = decltype(unary);
const auto higher1st = [](nullary_t f){ return f(); };
const auto higher2nd = [unary](nullary_t f1){
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
};
return higher1st(nullary) + higher2nd(nullary)(unary);
}
}
namespace test_variadic_templates
{
template <int...>
struct sum;
template <int N0, int... N1toN>
struct sum<N0, N1toN...>
{
static constexpr auto value = N0 + sum<N1toN...>::value;
};
template <>
struct sum<>
{
static constexpr auto value = 0;
};
static_assert(sum<>::value == 0, "");
static_assert(sum<1>::value == 1, "");
static_assert(sum<23>::value == 23, "");
static_assert(sum<1, 2>::value == 3, "");
static_assert(sum<5, 5, 11>::value == 21, "");
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
}
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
// because of this.
namespace test_template_alias_sfinae
{
struct foo {};
template<typename T>
using member = typename T::member_type;
template<typename T>
void func(...) {}
template<typename T>
void func(member<T>*) {}
void test();
void test() { func<foo>(0); }
}
} // namespace cxx11
#endif // __cplusplus >= 201103L
]])
dnl Tests for new features in C++14
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
// If the compiler admits that it is not ready for C++14, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201402L
#error "This is not a C++14 compiler"
#else
namespace cxx14
{
namespace test_polymorphic_lambdas
{
int
test()
{
const auto lambda = [](auto&&... args){
const auto istiny = [](auto x){
return (sizeof(x) == 1UL) ? 1 : 0;
};
const int aretiny[] = { istiny(args)... };
return aretiny[0];
};
return lambda(1, 1L, 1.0f, '1');
}
}
namespace test_binary_literals
{
constexpr auto ivii = 0b0000000000101010;
static_assert(ivii == 42, "wrong value");
}
namespace test_generalized_constexpr
{
template < typename CharT >
constexpr unsigned long
strlen_c(const CharT *const s) noexcept
{
auto length = 0UL;
for (auto p = s; *p; ++p)
++length;
return length;
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("x") == 1UL, "");
static_assert(strlen_c("test") == 4UL, "");
static_assert(strlen_c("another\0test") == 7UL, "");
}
namespace test_lambda_init_capture
{
int
test()
{
auto x = 0;
const auto lambda1 = [a = x](int b){ return a + b; };
const auto lambda2 = [a = lambda1(x)](){ return a; };
return lambda2();
}
}
namespace test_digit_seperators
{
constexpr auto ten_million = 100'000'000;
static_assert(ten_million == 100000000, "");
}
namespace test_return_type_deduction
{
auto f(int& x) { return x; }
decltype(auto) g(int& x) { return x; }
template < typename T1, typename T2 >
struct is_same
{
static constexpr auto value = false;
};
template < typename T >
struct is_same<T, T>
{
static constexpr auto value = true;
};
int
test()
{
auto x = 0;
static_assert(is_same<int, decltype(f(x))>::value, "");
static_assert(is_same<int&, decltype(g(x))>::value, "");
return x;
}
}
} // namespace cxx14
#endif // __cplusplus >= 201402L
]])

View File

@ -1,39 +0,0 @@
# ============================================================================
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
# ============================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the C++11
# standard; if necessary, add switches to CXX and CXXCPP to enable
# support.
#
# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
# macro with the version set to C++11. The two optional arguments are
# forwarded literally as the second and third argument respectively.
# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
# more information. If you want to use this macro, you also need to
# download the ax_cxx_compile_stdcxx.m4 file.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 17
AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])

View File

@ -8,7 +8,7 @@
<body lang="EN-US"> <body lang="EN-US">
<h2>Eclipse Foundation Software User Agreement</h2> <h2>Eclipse Foundation Software User Agreement</h2>
<p>February 1, 2011</p> <p>April 6, 2020</p>
<h3>Usage Of Content</h3> <h3>Usage Of Content</h3>
@ -21,8 +21,8 @@
<h3>Applicable Licenses</h3> <h3>Applicable Licenses</h3>
<p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0 <p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 2.0
(&quot;EPL&quot;). A copy of the EPL is provided with this Content and is also available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>. (&quot;EPL&quot;). A copy of the EPL is provided with this Content and is also available at <a href="https://www.eclipse.org/legal/epl-2.0/">https://www.eclipse.org/legal/epl-2.0/</a>.
For purposes of the EPL, &quot;Program&quot; will mean the Content.</p> For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code <p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code

View File

@ -7,10 +7,12 @@ set -ex
git clone https://github.com/eclipse/paho.mqtt.c.git git clone https://github.com/eclipse/paho.mqtt.c.git
cd paho.mqtt.c cd paho.mqtt.c
git checkout v1.3.1 git checkout v1.3.13
cmake -Bbuild -H. -DPAHO_WITH_SSL=ON -DPAHO_BUILD_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DPAHO_ENABLE_TESTING=OFF cmake -Bbuild -H. -DPAHO_WITH_SSL=ON -DPAHO_BUILD_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DPAHO_ENABLE_TESTING=OFF
sudo env "PATH=$PATH" cmake --build build/ --target install sudo env "PATH=$PATH" cmake --build build/ --target install
sudo ldconfig sudo ldconfig
exit 0 exit 0

View File

@ -7,14 +7,14 @@
#******************************************************************************* #*******************************************************************************
# Copyright (c) 2016-2017, Guilherme Maciel Ferreira # Copyright (c) 2016-2017, Guilherme Maciel Ferreira
# Copyright (c) 2017-2018, Frank Pagliughi # Copyright (c) 2017-2024, Frank Pagliughi
# #
# All rights reserved. This program and the accompanying materials # All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0 # are made available under the terms of the Eclipse Public License v2.0
# and Eclipse Distribution License v1.0 which accompany this distribution. # and Eclipse Distribution License v1.0 which accompany this distribution.
# #
# The Eclipse Public License is available at # The Eclipse Public License is available at
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v20.html
# and the Eclipse Distribution License is available at # and the Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php. # http://www.eclipse.org/org/documents/edl-v10.php.
# #
@ -23,123 +23,139 @@
# Frank Pagliughi - made the shared library optional # Frank Pagliughi - made the shared library optional
#*******************************************************************************/ #*******************************************************************************/
find_package(PahoMqttC REQUIRED)
# --- The headers ---
add_subdirectory(mqtt)
## --- Library dependencies --- ## --- Library dependencies ---
set (THREADS_PREFER_PTHREAD_FLAG ON) set (THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
#set(LIBS_SYSTEM Threads::Threads) include(GenerateExportHeader)
#if(WIN32)
# string(APPEND LIBS_SYSTEM " ws2_32")
#endif()
## --- Use object library to optimize compilation --- ## --- Use object library to optimize compilation ---
add_library(paho-cpp-objs OBJECT set(COMMON_SRC
async_client.cpp async_client.cpp
client.cpp client.cpp
connect_options.cpp
create_options.cpp
disconnect_options.cpp disconnect_options.cpp
iclient_persistence.cpp iclient_persistence.cpp
message.cpp message.cpp
properties.cpp properties.cpp
reason_code.cpp
response_options.cpp response_options.cpp
server_response.cpp
ssl_options.cpp ssl_options.cpp
string_collection.cpp string_collection.cpp
subscribe_options.cpp
token.cpp token.cpp
topic.cpp topic.cpp
connect_options.cpp
will_options.cpp will_options.cpp
) )
## install the shared library
#install(TARGETS paho-cpp-objs EXPORT PahoMqttCpp
# OBJECTS DESTINATION ${CMAKE_INSTALL_LIBDIR}
#)
# Object libraries can't use target_link_libraries in order to take advantage
# of transitive usage requirements until CMake 3.12. This is a workaround:
#target_include_directories(OBJS PRIVATE ${PAHO_MQTT_C_INCLUDE_DIRS})
target_include_directories(paho-cpp-objs
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>
PRIVATE
${PAHO_MQTT_C_INCLUDE_DIRS}
src
)
## --- Build the shared library, if requested --- ## --- Build the shared library, if requested ---
if(PAHO_BUILD_SHARED) if(PAHO_BUILD_SHARED)
## create the shared library message(STATUS "Creating shared library")
add_library(paho-mqttpp3 SHARED $<TARGET_OBJECTS:paho-cpp-objs>)
## add dependencies to the shared library ## Create the shared library
target_link_libraries(paho-mqttpp3 add_library(paho-mqttpp3-shared SHARED ${COMMON_SRC})
PRIVATE ${LIBS_SYSTEM} list(APPEND PAHO_CPP_TARGETS paho-mqttpp3-shared)
PUBLIC PahoMqttC::PahoMqttC Threads::Threads)
# It would be nice to exort the include paths from the obj lib, but we ## Alias for subdirectory builds
# get an export error. Perhaps in a future version? add_library(PahoMqttCpp::paho-mqttpp3-shared ALIAS paho-mqttpp3-shared)
# add_library(PahoMqttCpp::paho-mqttpp3 ALIAS paho-mqttpp3-shared)
# CMake Error: install(EXPORT "PahoMqttCpp" ...) includes target "paho-mqttpp3"
# which requires target "paho-cpp-objs" that is not in the export set.
#target_include_directories(paho-mqttpp3 PUBLIC target_compile_definitions(paho-mqttpp3-shared PRIVATE PAHO_MQTTPP_EXPORTS)
# $<TARGET_PROPERTY:paho-cpp-objs,INCLUDE_DIRECTORIES>
#)
target_include_directories(paho-mqttpp3 PUBLIC ## Add dependencies to the shared library
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> target_link_libraries(paho-mqttpp3-shared PUBLIC
$<INSTALL_INTERFACE:include> ${PAHO_MQTT_C_LIB}
Threads::Threads
${LIBS_SYSTEM}
) )
## set the shared library soname ## set the shared library soname
set_target_properties(paho-mqttpp3 PROPERTIES set_target_properties(paho-mqttpp3-shared PROPERTIES
OUTPUT_NAME paho-mqttpp3
VERSION ${PROJECT_VERSION} VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}) SOVERSION ${PROJECT_VERSION_MAJOR}
)
## install the shared library generate_export_header(paho-mqttpp3-shared
install(TARGETS paho-mqttpp3 EXPORT PahoMqttCpp BASE_NAME paho_mqttpp
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT_FILE_NAME ${PAHO_MQTTPP_GENERATED_DIR}/include/mqtt/export.h
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} )
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif() endif()
## --- Build static version of the library, if requested --- ## --- Build static version of the library, if requested ---
if(PAHO_BUILD_STATIC) if(PAHO_BUILD_STATIC)
## create the static library ## Create the static library
add_library(paho-mqttpp3-static STATIC $<TARGET_OBJECTS:paho-cpp-objs>) add_library(paho-mqttpp3-static STATIC ${COMMON_SRC})
list(APPEND PAHO_CPP_TARGETS paho-mqttpp3-static)
## add dependencies to the shared library ## Alias for subdirectory builds
target_link_libraries(paho-mqttpp3-static add_library(PahoMqttCpp::paho-mqttpp3-static ALIAS paho-mqttpp3-static)
PRIVATE ${LIBS_SYSTEM}
PUBLIC PahoMqttC::PahoMqttC Threads::Threads)
target_include_directories(paho-mqttpp3-static PUBLIC ## Add dependencies to the static library
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> target_link_libraries(paho-mqttpp3-static PUBLIC
$<INSTALL_INTERFACE:include> ${PAHO_MQTT_C_LIB}-static
Threads::Threads
${LIBS_SYSTEM}
) )
## install the static library if(${PAHO_BUILD_SHARED})
install(TARGETS paho-mqttpp3-static EXPORT PahoMqttCpp # This lib should configure for static exports
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} target_compile_definitions(paho-mqttpp3-static PRIVATE PAHO_MQTTPP_STATIC)
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) target_compile_definitions(paho-mqttpp3-static PRIVATE PAHO_MQTTPP_STATIC_DEFINE)
else()
# If no shared lib, make this one the default target
add_library(PahoMqttCpp::paho-mqttpp3 ALIAS paho-mqttpp3-static)
## Let the archive use the same name as the shared lib on *nix systems generate_export_header(paho-mqttpp3-static
BASE_NAME paho_mqttpp
EXPORT_FILE_NAME ${PAHO_MQTTPP_GENERATED_DIR}/include/mqtt/export.h
)
endif()
## Let the archive use the same base name as the shared lib on *nix systems
if(UNIX) if(UNIX)
set_target_properties(paho-mqttpp3-static PROPERTIES OUTPUT_NAME paho-mqttpp3) set_target_properties(paho-mqttpp3-static PROPERTIES OUTPUT_NAME paho-mqttpp3)
endif() endif()
endif() endif()
## --- Make sure at least one target was specified ---
if(NOT PAHO_CPP_TARGETS)
message(FATAL_ERROR "No build targets are specified")
endif()
## --- Set common properties, etc ---
include(GNUInstallDirs)
foreach(TARGET ${PAHO_CPP_TARGETS})
set_target_properties(${TARGET} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)
## Build warnings
target_compile_options(${TARGET} PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W3>
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra -Wdocumentation>
$<$<NOT:$<OR:$<CXX_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:Clang>>>:-Wall -Wextra>
)
target_include_directories(${TARGET} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
## install the shared library
install(TARGETS ${TARGET} EXPORT PahoMqttCpp
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endforeach()

File diff suppressed because it is too large Load Diff

View File

@ -2,14 +2,14 @@
// Implementation of the client class for the mqtt C++ client library. // Implementation of the client class for the mqtt C++ client library.
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2013-2017 Frank Pagliughi <fpagliughi@mindspring.com> * Copyright (c) 2013-2025 Frank Pagliughi <fpagliughi@mindspring.com>
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution. * and Eclipse Distribution License v1.0 which accompany this distribution.
* *
* The Eclipse Public License is available at * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php. * http://www.eclipse.org/org/documents/edl-v10.php.
* *
@ -18,131 +18,170 @@
*******************************************************************************/ *******************************************************************************/
#include "mqtt/client.h" #include "mqtt/client.h"
#include <memory>
#include <iostream> #include <iostream>
#include <memory>
namespace mqtt { namespace mqtt {
const std::chrono::minutes client::DFLT_TIMEOUT = std::chrono::minutes(5);
#if __cplusplus < 201703L
constexpr int client::DFLT_QOS;
#endif
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
PAHO_MQTTPP_EXPORT const std::chrono::seconds client::DFLT_TIMEOUT = std::chrono::seconds(30);
client::client(const string& serverURI, const string& clientId, PAHO_MQTTPP_EXPORT const int client::DFLT_QOS = 1;
iclient_persistence* persistence /*=nullptr*/)
: cli_(serverURI, clientId, persistence), // --------------------------------------------------------------------------
timeout_(DFLT_TIMEOUT), userCallback_(nullptr)
client::client(
const string& serverURI, const string& clientId /*=string{}*/,
const persistence_type& persistence /*=NO_PERSISTENCE*/
)
: cli_(serverURI, clientId, persistence), timeout_(DFLT_TIMEOUT), userCallback_(nullptr)
{ {
} }
client::client(const string& serverURI, const string& clientId, client::client(
const string& persistDir) const string& serverURI, const string& clientId, int maxBufferedMessages,
: cli_(serverURI, clientId, persistDir), const persistence_type& persistence /*=NO_PERSISTENCE*/
timeout_(DFLT_TIMEOUT), userCallback_(nullptr) )
: cli_(serverURI, clientId, maxBufferedMessages, persistence),
timeout_(DFLT_TIMEOUT),
userCallback_(nullptr)
{ {
} }
client::client(const string& serverURI, const string& clientId, client::client(
int maxBufferedMessages, iclient_persistence* persistence /*=nullptr*/) const string& serverURI, const string& clientId, const create_options& opts,
: cli_(serverURI, clientId, maxBufferedMessages, persistence), const persistence_type& persistence /*=NO_PERSISTENCE*/
timeout_(DFLT_TIMEOUT), userCallback_(nullptr) )
: cli_(serverURI, clientId, opts, persistence),
timeout_(DFLT_TIMEOUT),
userCallback_(nullptr)
{ {
} }
client::client(const string& serverURI, const string& clientId, client::client(const create_options& opts)
int maxBufferedMessages, const string& persistDir) : cli_(opts), timeout_(DFLT_TIMEOUT), userCallback_(nullptr)
: cli_(serverURI, clientId, maxBufferedMessages, persistDir),
timeout_(DFLT_TIMEOUT), userCallback_(nullptr)
{ {
} }
// --------------------------------------------------------------------------
void client::set_callback(callback& cb) void client::set_callback(callback& cb)
{ {
userCallback_ = &cb; userCallback_ = &cb;
cli_.set_callback(*this); cli_.set_callback(*this);
} }
connect_response client::connect() connect_response client::connect()
{ {
cli_.start_consuming(); cli_.start_consuming();
auto tok = cli_.connect(); auto tok = cli_.connect();
tok->wait_for(timeout_); if (!tok->wait_for(timeout_))
return tok->get_connect_response(); throw timeout_error();
return tok->get_connect_response();
} }
connect_response client::connect(connect_options opts) connect_response client::connect(connect_options opts)
{ {
cli_.start_consuming(); cli_.start_consuming();
auto tok = cli_.connect(std::move(opts)); auto tok = cli_.connect(std::move(opts));
tok->wait_for(timeout_); if (!tok->wait_for(timeout_))
return tok->get_connect_response(); throw timeout_error();
return tok->get_connect_response();
} }
connect_response client::reconnect() connect_response client::reconnect()
{ {
auto tok = cli_.reconnect(); auto tok = cli_.reconnect();
tok->wait_for(timeout_); if (!tok->wait_for(timeout_))
return tok->get_connect_response(); throw timeout_error();
return tok->get_connect_response();
} }
subscribe_response client::subscribe(const string& topicFilter) subscribe_response client::subscribe(
const string& topicFilter, const subscribe_options& opts /*=subscribe_options()*/,
const properties& props /*=properties()*/
)
{ {
auto tok = cli_.subscribe(topicFilter, DFLT_QOS); auto tok = cli_.subscribe(topicFilter, DFLT_QOS, opts, props);
tok->wait_for(timeout_); if (!tok->wait_for(timeout_))
return tok->get_subscribe_response(); throw timeout_error();
return tok->get_subscribe_response();
} }
subscribe_response client::subscribe(const string& topicFilter, int qos) subscribe_response client::subscribe(
const string& topicFilter, int qos,
const subscribe_options& opts /*=subscribe_options()*/,
const properties& props /*=properties()*/
)
{ {
auto tok = cli_.subscribe(topicFilter, qos); auto tok = cli_.subscribe(topicFilter, qos, opts, props);
tok->wait_for(timeout_); if (!tok->wait_for(timeout_))
return tok->get_subscribe_response(); throw timeout_error();
return tok->get_subscribe_response();
} }
subscribe_response client::subscribe(const string_collection& topicFilters) subscribe_response client::subscribe(
const string_collection& topicFilters,
const std::vector<subscribe_options>& opts /*=std::vector<subscribe_options>()*/,
const properties& props /*=properties()*/
)
{ {
qos_collection qos; qos_collection qos;
for (size_t i=0; i<topicFilters.size(); ++i) for (size_t i = 0; i < topicFilters.size(); ++i) qos.push_back(DFLT_QOS);
qos.push_back(DFLT_QOS);
auto tok = cli_.subscribe(ptr(topicFilters), qos); auto tok = cli_.subscribe(ptr(topicFilters), qos, opts, props);
tok->wait_for(timeout_); if (!tok->wait_for(timeout_))
return tok->get_subscribe_response(); throw timeout_error();
return tok->get_subscribe_response();
} }
subscribe_response client::subscribe(const string_collection& topicFilters, subscribe_response client::subscribe(
const qos_collection& qos) const string_collection& topicFilters, const qos_collection& qos,
const std::vector<subscribe_options>& opts /*=std::vector<subscribe_options>()*/,
const properties& props /*=properties()*/
)
{ {
auto tok = cli_.subscribe(ptr(topicFilters), qos); auto tok = cli_.subscribe(ptr(topicFilters), qos, opts, props);
tok->wait_for(timeout_); if (!tok->wait_for(timeout_))
return tok->get_subscribe_response(); throw timeout_error();
return tok->get_subscribe_response();
} }
unsubscribe_response client::unsubscribe(const string& topicFilter) unsubscribe_response
client::unsubscribe(const string& topicFilter, const properties& props /*=properties()*/)
{ {
auto tok = cli_.unsubscribe(topicFilter); auto tok = cli_.unsubscribe(topicFilter, props);
tok->wait_for(timeout_); if (!tok->wait_for(timeout_))
return tok->get_unsubscribe_response(); throw timeout_error();
return tok->get_unsubscribe_response();
} }
unsubscribe_response client::unsubscribe(const string_collection& topicFilters) unsubscribe_response client::unsubscribe(
const string_collection& topicFilters, const properties& props /*=properties()*/
)
{ {
auto tok = cli_.unsubscribe(ptr(topicFilters)); auto tok = cli_.unsubscribe(ptr(topicFilters), props);
tok->wait_for(timeout_); if (!tok->wait_for(timeout_))
return tok->get_unsubscribe_response(); throw timeout_error();
return tok->get_unsubscribe_response();
} }
void client::disconnect() void client::disconnect()
{ {
cli_.disconnect()->wait_for(timeout_); cli_.stop_consuming();
cli_.stop_consuming(); if (!cli_.disconnect()->wait_for(timeout_))
throw timeout_error();
}
void client::disconnect(int timeoutMS)
{
cli_.stop_consuming();
if (!cli_.disconnect(timeoutMS)->wait_for(timeout_))
throw timeout_error();
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// end namespace mqtt // end namespace mqtt
} } // namespace mqtt

View File

@ -1,12 +1,15 @@
// connect_options.cpp
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2017-2024 Frank Pagliughi <fpagliughi@mindspring.com>
* Copyright (c) 2016 Guilherme M. Ferreira <guilherme.maciel.ferreira@gmail.com> * Copyright (c) 2016 Guilherme M. Ferreira <guilherme.maciel.ferreira@gmail.com>
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution. * and Eclipse Distribution License v1.0 which accompany this distribution.
* *
* The Eclipse Public License is available at * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php. * http://www.eclipse.org/org/documents/edl-v10.php.
* *
@ -16,194 +19,388 @@
*******************************************************************************/ *******************************************************************************/
#include "mqtt/connect_options.h" #include "mqtt/connect_options.h"
#include <cstring> #include <cstring>
namespace mqtt { namespace mqtt {
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
const MQTTAsync_connectOptions connect_options::DFLT_C_STRUCT = MQTTAsync_connectOptions_initializer; connect_options::connect_options(int ver /*=MQTTVERSION_DEFAULT*/)
connect_options::connect_options() : opts_(DFLT_C_STRUCT)
{ {
opts_ = (ver < MQTTVERSION_5) ? DFLT_C_STRUCT : DFLT_C_STRUCT5;
} }
connect_options::connect_options(string_ref userName, binary_ref password) connect_options::connect_options(
: connect_options() string_ref userName, binary_ref password, int ver /*=MQTTVERSION_DEFAULT*/
)
: connect_options(ver)
{ {
set_user_name(userName); set_user_name(userName);
set_password(password); set_password(password);
} }
connect_options::connect_options(const connect_options& opt) : opts_(opt.opts_) connect_options::connect_options(const connect_options& opt)
: opts_(opt.opts_),
userName_(opt.userName_),
password_(opt.password_),
tok_(opt.tok_),
serverURIs_(opt.serverURIs_),
props_(opt.props_),
httpHeaders_(opt.httpHeaders_),
httpProxy_(opt.httpProxy_),
httpsProxy_(opt.httpsProxy_)
{ {
if (opts_.will) if (opts_.will)
set_will(opt.will_); set_will(opt.will_);
if (opts_.ssl) if (opts_.ssl)
set_ssl(opt.ssl_); set_ssl(opt.ssl_);
set_user_name(opt.userName_); update_c_struct();
set_password(opt.password_);
} }
connect_options::connect_options(connect_options&& opt) : opts_(opt.opts_), connect_options::connect_options(connect_options&& opt)
will_(std::move(opt.will_)), : opts_(opt.opts_),
ssl_(std::move(opt.ssl_)), will_(std::move(opt.will_)),
userName_(std::move(opt.userName_)), ssl_(std::move(opt.ssl_)),
password_(std::move(opt.password_)) userName_(std::move(opt.userName_)),
password_(std::move(opt.password_)),
tok_(std::move(opt.tok_)),
serverURIs_(std::move(opt.serverURIs_)),
props_(std::move(opt.props_)),
httpHeaders_(std::move(opt.httpHeaders_)),
httpProxy_(std::move(opt.httpProxy_)),
httpsProxy_(std::move(opt.httpsProxy_))
{ {
if (opts_.will) { if (opts_.will)
opts_.will = &will_.opts_; opts_.will = &will_.opts_;
opts_.willProperties = const_cast<MQTTProperties*>(&will_.props_.c_struct());
}
if (opts_.ssl) if (opts_.willProperties)
opts_.ssl = &ssl_.opts_; opts_.willProperties = const_cast<MQTTProperties*>(&will_.props_.c_struct());
opts_.username = c_str(userName_); if (opts_.ssl)
set_password(password_); opts_.ssl = &ssl_.opts_;
update_c_struct();
}
// Unfortunately, with the existing implementation, there's no way to know
// if the (connect) properties, will and ssl options were set by looking at the C++ structs.
// In a major update, we can consider using a pointer or optional<> to
// indicate that they were set.
// But, for now, the copy and assignment operations must handle it manually
// by looking to see if the source C options pointer was set.
void connect_options::update_c_struct()
{
opts_.username = c_str(userName_);
// Password
if (password_.empty()) {
opts_.binarypwd.len = 0;
opts_.binarypwd.data = nullptr;
}
else {
opts_.binarypwd.len = (int)password_.size();
opts_.binarypwd.data = password_.data();
}
// Token
opts_.onSuccess = nullptr;
opts_.onFailure = nullptr;
opts_.onSuccess5 = nullptr;
opts_.onFailure5 = nullptr;
if (tok_) {
if (opts_.MQTTVersion < MQTTVERSION_5) {
opts_.onSuccess = &token::on_success;
opts_.onFailure = &token::on_failure;
}
else {
opts_.onSuccess5 = &token::on_success5;
opts_.onFailure5 = &token::on_failure5;
}
}
// Server URIs
if (!serverURIs_ || serverURIs_->empty()) {
opts_.serverURIcount = 0;
opts_.serverURIs = nullptr;
}
else {
opts_.serverURIcount = (int)serverURIs_->size();
opts_.serverURIs = serverURIs_->c_arr();
}
// Connect Properties
if (opts_.MQTTVersion >= MQTTVERSION_5)
opts_.connectProperties = const_cast<MQTTProperties*>(&props_.c_struct());
// HTTP & Proxy
opts_.httpProxy = c_str(httpProxy_);
opts_.httpsProxy = c_str(httpsProxy_);
} }
connect_options& connect_options::operator=(const connect_options& opt) connect_options& connect_options::operator=(const connect_options& opt)
{ {
opts_ = opt.opts_; if (&opt == this)
return *this;
if (opts_.will) opts_ = opt.opts_;
set_will(opt.will_);
if (opts_.ssl) if (opts_.will)
set_ssl(opt.ssl_); set_will(opt.will_);
set_user_name(opt.userName_); if (opts_.ssl)
set_password(opt.password_); set_ssl(opt.ssl_);
return *this; userName_ = opt.userName_;
password_ = opt.password_;
tok_ = opt.tok_;
serverURIs_ = opt.serverURIs_;
props_ = opt.props_;
httpHeaders_ = opt.httpHeaders_;
httpProxy_ = opt.httpProxy_;
httpsProxy_ = opt.httpsProxy_;
update_c_struct();
return *this;
} }
connect_options& connect_options::operator=(connect_options&& opt) connect_options& connect_options::operator=(connect_options&& opt)
{ {
opts_ = opt.opts_; if (&opt == this)
return *this;
if (opts_.will) opts_ = opt.opts_;
set_will(std::move(opt.will_));
if (opts_.ssl) if (opts_.will)
set_ssl(std::move(opt.ssl_)); set_will(std::move(opt.will_));
userName_ = std::move(opt.userName_); if (opts_.ssl)
opts_.username = c_str(userName_); set_ssl(std::move(opt.ssl_));
password_ = std::move(opt.password_); userName_ = std::move(opt.userName_);
set_password(password_); password_ = std::move(opt.password_);
return *this; tok_ = std::move(opt.tok_);
serverURIs_ = std::move(opt.serverURIs_);
props_ = std::move(opt.props_);
httpHeaders_ = std::move(opt.httpHeaders_);
httpProxy_ = std::move(opt.httpProxy_);
httpsProxy_ = std::move(opt.httpsProxy_);
update_c_struct();
return *this;
} }
void connect_options::set_will(const will_options& will) void connect_options::set_will(const will_options& will)
{ {
will_ = will; will_ = will;
opts_.will = &will_.opts_; opts_.will = &will_.opts_;
opts_.willProperties = will_.get_properties().empty() opts_.willProperties = will_.get_properties().empty()
? nullptr : const_cast<MQTTProperties*>(&will_.props_.c_struct()); ? nullptr
: const_cast<MQTTProperties*>(&will_.props_.c_struct());
} }
void connect_options::set_will(will_options&& will) void connect_options::set_will(will_options&& will)
{ {
will_ = will; will_ = will;
opts_.will = &will_.opts_; opts_.will = &will_.opts_;
opts_.willProperties = will_.get_properties().empty() opts_.willProperties = will_.get_properties().empty()
? nullptr : const_cast<MQTTProperties*>(&will_.props_.c_struct()); ? nullptr
: const_cast<MQTTProperties*>(&will_.props_.c_struct());
} }
void connect_options::set_user_name(string_ref userName) void connect_options::set_user_name(string_ref userName)
{ {
userName_ = std::move(userName); userName_ = std::move(userName);
opts_.username = c_str(userName_); opts_.username = c_str(userName_);
} }
void connect_options::set_password(binary_ref password) void connect_options::set_password(binary_ref password)
{ {
password_ = std::move(password); password_ = std::move(password);
if (password_.empty()) { if (password_.empty()) {
opts_.binarypwd.len = 0; opts_.binarypwd.len = 0;
opts_.binarypwd.data = nullptr; opts_.binarypwd.data = nullptr;
} }
else { else {
opts_.binarypwd.len = (int) password_.size(); opts_.binarypwd.len = (int)password_.size();
opts_.binarypwd.data = password_.data(); opts_.binarypwd.data = password_.data();
} }
} }
void connect_options::set_ssl(const ssl_options& ssl) void connect_options::set_ssl(const ssl_options& ssl)
{ {
ssl_ = ssl; ssl_ = ssl;
opts_.ssl = &ssl_.opts_; opts_.ssl = &ssl_.opts_;
} }
void connect_options::set_ssl(ssl_options&& ssl) void connect_options::set_ssl(ssl_options&& ssl)
{ {
ssl_ = ssl; ssl_ = ssl;
opts_.ssl = &ssl_.opts_; opts_.ssl = &ssl_.opts_;
}
// Clean sessions only apply to MQTT v3, so force it there if set.
void connect_options::set_clean_session(bool clean)
{
if (opts_.MQTTVersion < MQTTVERSION_5)
opts_.cleansession = to_int(clean);
}
// Clean start only apply to MQTT v5, so force it there if set.
void connect_options::set_clean_start(bool cleanStart)
{
if (opts_.MQTTVersion >= MQTTVERSION_5)
opts_.cleanstart = to_int(cleanStart);
} }
void connect_options::set_token(const token_ptr& tok) void connect_options::set_token(const token_ptr& tok)
{ {
tok_ = tok; tok_ = tok;
opts_.context = tok_.get(); opts_.context = tok_.get();
opts_.onSuccess = nullptr; opts_.onSuccess = nullptr;
opts_.onFailure = nullptr; opts_.onFailure = nullptr;
opts_.onSuccess5 = nullptr; opts_.onSuccess5 = nullptr;
opts_.onFailure5 = nullptr; opts_.onFailure5 = nullptr;
if (tok) { if (tok) {
if (opts_.MQTTVersion < MQTTVERSION_5) { if (opts_.MQTTVersion < MQTTVERSION_5) {
opts_.onSuccess = &token::on_success; opts_.onSuccess = &token::on_success;
opts_.onFailure = &token::on_failure; opts_.onFailure = &token::on_failure;
} }
else { else {
opts_.onSuccess5 = &token::on_success5; opts_.onSuccess5 = &token::on_success5;
opts_.onFailure5 = &token::on_failure5; opts_.onFailure5 = &token::on_failure5;
} }
} }
} }
void connect_options::set_servers(const_string_collection_ptr serverURIs) void connect_options::set_servers(const_string_collection_ptr serverURIs)
{ {
if (serverURIs) { if (serverURIs) {
serverURIs_ = std::move(serverURIs); serverURIs_ = std::move(serverURIs);
opts_. serverURIcount = (int) serverURIs_->size(); opts_.serverURIcount = (int)serverURIs_->size();
opts_.serverURIs = serverURIs_->c_arr(); opts_.serverURIs = serverURIs_->c_arr();
} }
else { else {
serverURIs_.reset(); serverURIs_.reset();
opts_.serverURIcount = 0; opts_.serverURIcount = 0;
opts_.serverURIs = nullptr; opts_.serverURIs = nullptr;
} }
} }
void connect_options::set_mqtt_version(int mqttVersion) { void connect_options::set_mqtt_version(int mqttVersion)
opts_.MQTTVersion = mqttVersion;
if (mqttVersion < MQTTVERSION_5)
opts_.cleanstart = 0;
else
opts_.cleansession = 0;
}
void connect_options::set_automatic_reconnect(int minRetryInterval,
int maxRetryInterval)
{ {
opts_.automaticReconnect = to_int(true); opts_.MQTTVersion = mqttVersion;
opts_.minRetryInterval = minRetryInterval;
opts_.maxRetryInterval = maxRetryInterval; if (mqttVersion < MQTTVERSION_5)
opts_.cleanstart = 0;
else
opts_.cleansession = 0;
} }
void connect_options::set_automatic_reconnect(int minRetryInterval, int maxRetryInterval)
{
opts_.automaticReconnect = to_int(true);
opts_.minRetryInterval = minRetryInterval;
opts_.maxRetryInterval = maxRetryInterval;
}
void connect_options::set_properties(const properties& props)
{
props_ = props;
opts_.connectProperties = const_cast<MQTTProperties*>(&props_.c_struct());
opts_.MQTTVersion = MQTTVERSION_5;
}
void connect_options::set_properties(properties&& props)
{
props_ = std::move(props);
opts_.connectProperties = const_cast<MQTTProperties*>(&props_.c_struct());
opts_.MQTTVersion = MQTTVERSION_5;
}
void connect_options::set_http_proxy(const string& httpProxy)
{
httpProxy_ = httpProxy;
opts_.httpProxy = c_str(httpProxy_);
}
void connect_options::set_https_proxy(const string& httpsProxy)
{
httpsProxy_ = httpsProxy;
opts_.httpsProxy = c_str(httpsProxy_);
}
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// connect_data
} // end namespace mqtt connect_data::connect_data(string_ref userName) : userName_(userName) { update_c_struct(); }
connect_data::connect_data(string_ref userName, binary_ref password)
: userName_(userName), password_(password)
{
update_c_struct();
}
connect_data::connect_data(const MQTTAsync_connectData& cdata)
: password_((char*)cdata.binarypwd.data, size_t(cdata.binarypwd.len))
{
if (cdata.username)
userName_ = string_ref(cdata.username, strlen(cdata.username));
update_c_struct();
}
void connect_data::update_c_struct()
{
data_.username = userName_.empty() ? nullptr : userName_.c_str();
if (password_.empty()) {
data_.binarypwd.len = 0;
data_.binarypwd.data = nullptr;
}
else {
data_.binarypwd.len = (int)password_.size();
data_.binarypwd.data = password_.data();
}
}
connect_data& connect_data::operator=(const connect_data& rhs)
{
if (&rhs != this) {
userName_ = rhs.userName_;
password_ = rhs.password_;
update_c_struct();
}
return *this;
}
void connect_data::set_user_name(string_ref userName)
{
userName_ = std::move(userName);
update_c_struct();
}
void connect_data::set_password(binary_ref password)
{
password_ = std::move(password);
update_c_struct();
}
/////////////////////////////////////////////////////////////////////////////
} // end namespace mqtt

60
src/create_options.cpp Normal file
View File

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2020-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include "mqtt/create_options.h"
#include <cstring>
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
create_options::create_options(int mqttVersion, int maxBufferedMessages)
{
opts_.MQTTVersion = mqttVersion;
if (maxBufferedMessages != 0) {
opts_.sendWhileDisconnected = to_int(true);
opts_.maxBufferedMessages = maxBufferedMessages;
}
}
// --------------------------------------------------------------------------
create_options& create_options::operator=(const create_options& rhs)
{
if (&rhs != this) {
opts_ = rhs.opts_;
serverURI_ = rhs.serverURI_;
clientId_ = rhs.clientId_;
persistence_ = rhs.persistence_;
}
return *this;
}
create_options& create_options::operator=(create_options&& rhs)
{
if (&rhs != this) {
opts_ = std::move(rhs.opts_);
serverURI_ = std::move(rhs.serverURI_);
clientId_ = std::move(rhs.clientId_);
persistence_ = std::move(rhs.persistence_);
}
return *this;
}
/////////////////////////////////////////////////////////////////////////////
} // end namespace mqtt

View File

@ -1,67 +1,73 @@
// disconnect_options.cpp // disconnect_options.cpp
#include "mqtt/disconnect_options.h" #include "mqtt/disconnect_options.h"
#include <utility>
#include <cstring> #include <cstring>
#include <utility>
namespace mqtt { namespace mqtt {
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// disconnect_options
const MQTTAsync_disconnectOptions disconnect_options::DFLT_C_STRUCT = MQTTAsync_disconnectOptions_initializer;
disconnect_options::disconnect_options() : opts_(DFLT_C_STRUCT)
{
}
disconnect_options::disconnect_options(const disconnect_options& opt) disconnect_options::disconnect_options(const disconnect_options& opt)
: opts_(opt.opts_), tok_(opt.tok_) : opts_(opt.opts_), tok_(opt.tok_), props_(opt.props_)
{ {
update_c_struct();
} }
disconnect_options::disconnect_options(disconnect_options&& opt) disconnect_options::disconnect_options(disconnect_options&& opt)
: opts_(opt.opts_), tok_(std::move(opt.tok_)) : opts_(opt.opts_), tok_(std::move(opt.tok_)), props_(std::move(opt.props_))
{ {
update_c_struct();
}
void disconnect_options::update_c_struct()
{
opts_.properties = props_.c_struct();
opts_.context = tok_.get();
} }
disconnect_options& disconnect_options::operator=(const disconnect_options& opt) disconnect_options& disconnect_options::operator=(const disconnect_options& opt)
{ {
opts_ = opt.opts_; opts_ = opt.opts_;
tok_ = opt.tok_; tok_ = opt.tok_;
return *this; props_ = opt.props_;
update_c_struct();
return *this;
} }
disconnect_options& disconnect_options::operator=(disconnect_options&& opt) disconnect_options& disconnect_options::operator=(disconnect_options&& opt)
{ {
opts_ = opt.opts_; opts_ = opt.opts_;
tok_ = std::move(opt.tok_); tok_ = std::move(opt.tok_);
return *this; props_ = std::move(opt.props_);
update_c_struct();
return *this;
} }
void disconnect_options::set_token(const token_ptr& tok, int mqttVersion) void disconnect_options::set_token(const token_ptr& tok, int mqttVersion)
{ {
tok_ = tok; tok_ = tok;
opts_.context = tok_.get(); opts_.context = tok_.get();
opts_.onSuccess = nullptr; opts_.onSuccess = nullptr;
opts_.onFailure = nullptr; opts_.onFailure = nullptr;
opts_.onSuccess5 = nullptr; opts_.onSuccess5 = nullptr;
opts_.onFailure5 = nullptr; opts_.onFailure5 = nullptr;
if (tok) { if (tok) {
if (mqttVersion >= MQTTVERSION_5) { if (mqttVersion >= MQTTVERSION_5) {
opts_.onSuccess5 = &token::on_success5; opts_.onSuccess5 = &token::on_success5;
opts_.onFailure5 = &token::on_failure5; opts_.onFailure5 = &token::on_failure5;
} }
else { else {
opts_.onSuccess = &token::on_success; opts_.onSuccess = &token::on_success;
opts_.onFailure = &token::on_failure; opts_.onFailure = &token::on_failure;
} }
} }
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// end namespace 'mqtt' } // namespace mqtt
}

View File

@ -4,22 +4,27 @@
* Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com> * Copyright (c) 2013-2016 Frank Pagliughi <fpagliughi@mindspring.com>
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution. * and Eclipse Distribution License v1.0 which accompany this distribution.
* *
* The Eclipse Public License is available at * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php. * http://www.eclipse.org/org/documents/edl-v10.php.
* *
* Contributors: * Contributors:
* Frank Pagliughi - initial implementation and documentation * Frank Pagliughi - initial implementation and documentation
*******************************************************************************/ *******************************************************************************/
#include "mqtt/types.h"
#include "mqtt/iclient_persistence.h" #include "mqtt/iclient_persistence.h"
#include <cstring>
#include <cstdlib> #include <cstdlib>
#include <cstring>
#include <vector>
#include "mqtt/types.h"
using namespace std;
namespace mqtt { namespace mqtt {
@ -30,122 +35,143 @@ namespace mqtt {
// C++ persistence object, which is reassigned to the 'handle'. Subsequent // C++ persistence object, which is reassigned to the 'handle'. Subsequent
// calls have the object address as the handle. // calls have the object address as the handle.
int iclient_persistence::persistence_open(void** handle, const char* clientID, int iclient_persistence::persistence_open(
const char* serverURI, void* context) void** handle, const char* clientID, const char* serverURI, void* context
)
{ {
try { try {
if (handle && clientID && serverURI && context) { if (handle && clientID && serverURI && context) {
static_cast<iclient_persistence*>(context)->open(clientID, serverURI); static_cast<iclient_persistence*>(context)->open(clientID, serverURI);
*handle = context; *handle = context;
return MQTTASYNC_SUCCESS; return MQTTASYNC_SUCCESS;
} }
} }
catch (...) {} catch (...) {
}
return MQTTCLIENT_PERSISTENCE_ERROR; return MQTTCLIENT_PERSISTENCE_ERROR;
} }
int iclient_persistence::persistence_close(void* handle) int iclient_persistence::persistence_close(void* handle)
{ {
try { try {
if (handle) { if (handle) {
static_cast<iclient_persistence*>(handle)->close(); static_cast<iclient_persistence*>(handle)->close();
return MQTTASYNC_SUCCESS; return MQTTASYNC_SUCCESS;
} }
} }
catch (...) {} catch (...) {
}
return MQTTCLIENT_PERSISTENCE_ERROR; return MQTTCLIENT_PERSISTENCE_ERROR;
} }
int iclient_persistence::persistence_put(void* handle, char* key, int bufcount, int iclient_persistence::persistence_put(
char* buffers[], int buflens[]) void* handle, char* key, int bufcount, char* buffers[], int buflens[]
)
{ {
try { try {
if (handle && bufcount > 0 && buffers && buflens) { if (handle && bufcount > 0 && buffers && buflens) {
std::vector<string_view> vec; std::vector<string_view> vec;
for (int i=0; i<bufcount; ++i) for (int i = 0; i < bufcount; ++i)
vec.push_back(string_view(buffers[i], buflens[i])); vec.push_back(string_view(buffers[i], buflens[i]));
static_cast<iclient_persistence*>(handle)->put(key, vec); static_cast<iclient_persistence*>(handle)->put(key, vec);
return MQTTASYNC_SUCCESS; return MQTTASYNC_SUCCESS;
} }
} }
catch (...) {} catch (...) {
}
return MQTTCLIENT_PERSISTENCE_ERROR; return MQTTCLIENT_PERSISTENCE_ERROR;
} }
int iclient_persistence::persistence_get(void* handle, char* key, int iclient_persistence::persistence_get(void* handle, char* key, char** buffer, int* buflen)
char** buffer, int* buflen)
{ {
try { try {
if (handle && key && buffer && buflen) { if (handle && key && buffer && buflen) {
auto sv = static_cast<iclient_persistence*>(handle)->get(key); auto s = static_cast<iclient_persistence*>(handle)->get(key);
*buffer = const_cast<char*>(sv.data()); size_t n = s.length();
*buflen = (int) sv.length(); *buffer = static_cast<char*>(MQTTAsync_malloc(n));
return MQTTASYNC_SUCCESS; memcpy(*buffer, s.data(), n);
} *buflen = int(n);
} return MQTTASYNC_SUCCESS;
catch (...) {} }
}
catch (...) {
}
return MQTTCLIENT_PERSISTENCE_ERROR; return MQTTCLIENT_PERSISTENCE_ERROR;
} }
int iclient_persistence::persistence_remove(void* handle, char* key) int iclient_persistence::persistence_remove(void* handle, char* key)
{ {
try { try {
if (handle && key) { if (handle && key) {
static_cast<iclient_persistence*>(handle)->remove(key); static_cast<iclient_persistence*>(handle)->remove(key);
return MQTTASYNC_SUCCESS; return MQTTASYNC_SUCCESS;
} }
} }
catch (...) {} catch (...) {
}
return MQTTCLIENT_PERSISTENCE_ERROR; return MQTTCLIENT_PERSISTENCE_ERROR;
} }
int iclient_persistence::persistence_keys(void* handle, char*** keys, int* nkeys) int iclient_persistence::persistence_keys(void* handle, char*** keys, int* nkeys)
{ {
try { try {
if (handle && keys && nkeys) { if (handle && keys && nkeys) {
auto& k = static_cast<iclient_persistence*>(handle)->keys(); auto k = static_cast<iclient_persistence*>(handle)->keys();
size_t n = k.size(); size_t n = k.size();
*nkeys = n; *nkeys = int(n);
*keys = (n == 0) ? nullptr : const_cast<char**>(k.c_arr()); if (n == 0) {
return MQTTASYNC_SUCCESS; *keys = nullptr;
} }
} else {
catch (...) {} *keys = static_cast<char**>(MQTTAsync_malloc(n * sizeof(char*)));
for (size_t i = 0; i < n; ++i) {
auto sz = k[i].size();
char* buf = static_cast<char*>(MQTTAsync_malloc(sz + 1));
strncpy(buf, k[i].c_str(), sz + 1);
buf[sz] = '\0';
(*keys)[i] = buf;
}
}
return MQTTASYNC_SUCCESS;
}
}
catch (...) {
}
return MQTTCLIENT_PERSISTENCE_ERROR; return MQTTCLIENT_PERSISTENCE_ERROR;
} }
int iclient_persistence::persistence_clear(void* handle) int iclient_persistence::persistence_clear(void* handle)
{ {
try { try {
if (handle) { if (handle) {
static_cast<iclient_persistence*>(handle)->clear(); static_cast<iclient_persistence*>(handle)->clear();
return MQTTASYNC_SUCCESS; return MQTTASYNC_SUCCESS;
} }
} }
catch (...) {} catch (...) {
}
return MQTTCLIENT_PERSISTENCE_ERROR; return MQTTCLIENT_PERSISTENCE_ERROR;
} }
int iclient_persistence::persistence_containskey(void* handle, char* key) int iclient_persistence::persistence_containskey(void* handle, char* key)
{ {
try { try {
if (handle && key && if (handle && key && static_cast<iclient_persistence*>(handle)->contains_key(key))
static_cast<iclient_persistence*>(handle)->contains_key(key)) return MQTTASYNC_SUCCESS;
return MQTTASYNC_SUCCESS; }
} catch (...) {
catch (...) {} }
return MQTTCLIENT_PERSISTENCE_ERROR; return MQTTCLIENT_PERSISTENCE_ERROR;
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// end namespace mqtt // end namespace mqtt
} } // namespace mqtt

View File

@ -1,14 +1,14 @@
// message.cpp // message.cpp
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2013-2017 Frank Pagliughi <fpagliughi@mindspring.com> * Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution. * and Eclipse Distribution License v1.0 which accompany this distribution.
* *
* The Eclipse Public License is available at * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php. * http://www.eclipse.org/org/documents/edl-v10.php.
* *
@ -16,107 +16,114 @@
* Frank Pagliughi - initial implementation and documentation * Frank Pagliughi - initial implementation and documentation
*******************************************************************************/ *******************************************************************************/
#include "mqtt/message.h" #include "mqtt/message.h"
#include <cstring> #include <cstring>
#include <utility> #include <utility>
#include "mqtt/export.h"
namespace mqtt { namespace mqtt {
// A const string to use for references
PAHO_MQTTPP_EXPORT const string message::EMPTY_STR;
// A const binary to use for references
PAHO_MQTTPP_EXPORT const binary message::EMPTY_BIN;
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
#if __cplusplus < 201703L message::message(
constexpr int message::DFLT_QOS; string_ref topic, const void* payload, size_t len, int qos, bool retained,
constexpr bool message::DFLT_RETAINED; const properties& props /*=properties()*/
#endif )
: topic_(std::move(topic))
const MQTTAsync_message message::DFLT_C_STRUCT = MQTTAsync_message_initializer;
// --------------------------------------------------------------------------
message::message() : msg_(DFLT_C_STRUCT)
{ {
set_payload(payload, len);
set_qos(qos);
set_retained(retained);
set_properties(props);
} }
message::message(string_ref topic, const void* payload, size_t len, int qos, bool retained) message::message(
: msg_(DFLT_C_STRUCT), topic_(std::move(topic)) string_ref topic, binary_ref payload, int qos, bool retained,
const properties& props /*=properties()*/
)
: topic_(std::move(topic))
{ {
set_payload(payload, len); set_payload(std::move(payload));
set_qos(qos); set_qos(qos);
set_retained(retained); set_retained(retained);
set_properties(props);
} }
message::message(string_ref topic, binary_ref payload, int qos, bool retained) message::message(string_ref topic, const MQTTAsync_message& cmsg)
: msg_(DFLT_C_STRUCT), topic_(std::move(topic)) : msg_(cmsg), topic_(std::move(topic)), props_(cmsg.properties)
{ {
set_payload(std::move(payload)); set_payload(cmsg.payload, cmsg.payloadlen);
set_qos(qos); msg_.properties = props_.c_struct();
set_retained(retained);
} }
message::message(string_ref topic, const MQTTAsync_message& msg) message::message(const message& other)
: msg_(msg), topic_(std::move(topic)), props_(msg.properties) : msg_(other.msg_), topic_(other.topic_), props_(other.props_)
{ {
msg_.properties = props_.c_struct(); set_payload(other.payload_);
set_payload(msg.payload, msg.payloadlen); msg_.properties = props_.c_struct();
} }
message::message(const message& other) : msg_(other.msg_), topic_(other.topic_) message::message(message&& other)
: msg_(other.msg_), topic_(std::move(other.topic_)), props_(std::move(other.props_))
{ {
set_payload(other.payload_); set_payload(std::move(other.payload_));
} other.msg_.payloadlen = 0;
other.msg_.payload = nullptr;
message::message(message&& other) : msg_(other.msg_), topic_(std::move(other.topic_)) msg_.properties = props_.c_struct();
{
set_payload(std::move(other.payload_));
other.msg_.payloadlen = 0;
other.msg_.payload = nullptr;
} }
message& message::operator=(const message& rhs) message& message::operator=(const message& rhs)
{ {
if (&rhs != this) { if (&rhs != this) {
msg_ = rhs.msg_; msg_ = rhs.msg_;
topic_ = rhs.topic_; topic_ = rhs.topic_;
set_payload(rhs.payload_); set_payload(rhs.payload_);
} set_properties(rhs.props_);
return *this; }
return *this;
} }
message& message::operator=(message&& rhs) message& message::operator=(message&& rhs)
{ {
if (&rhs != this) { if (&rhs != this) {
msg_ = rhs.msg_; msg_ = rhs.msg_;
topic_ = std::move(rhs.topic_); topic_ = std::move(rhs.topic_);
set_payload(std::move(rhs.payload_)); set_payload(std::move(rhs.payload_));
rhs.msg_.payloadlen = 0; set_properties(std::move(rhs.props_));
rhs.msg_.payload = nullptr;
} rhs.msg_ = DFLT_C_STRUCT;
return *this; }
return *this;
} }
void message::clear_payload() void message::clear_payload()
{ {
payload_.reset(); payload_.reset();
msg_.payload = nullptr; msg_.payload = nullptr;
msg_.payloadlen = 0; msg_.payloadlen = 0;
} }
void message::set_payload(binary_ref payload) void message::set_payload(binary_ref payload)
{ {
payload_ = std::move(payload); payload_ = std::move(payload);
if (payload_.empty()) { if (payload_.empty()) {
msg_.payload = nullptr; msg_.payload = nullptr;
msg_.payloadlen = 0; msg_.payloadlen = 0;
} }
else { else {
msg_.payload = const_cast<binary_ref::value_type*>(payload_.data()); msg_.payload = const_cast<binary_ref::value_type*>(payload_.data());
msg_.payloadlen = payload_.length(); msg_.payloadlen = int(payload_.length());
} }
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// end namespace mqtt } // namespace mqtt
}

Some files were not shown because too many files have changed in this diff Show More