SNode.C
Loading...
Searching...
No Matches
Broker.cpp
Go to the documentation of this file.
1/*
2 * SNode.C - a slim toolkit for network communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "iot/mqtt/server/broker/Broker.h"
21
22#ifndef DOXYGEN_SHOULD_SKIP_THIS
23
24#include "log/Logger.h"
25
26#include <algorithm>
27#include <cstdio>
28#include <cstdlib>
29#include <fstream>
30#include <nlohmann/json.hpp>
31
32// IWYU pragma: no_include <nlohmann/json_fwd.hpp>
33// IWYU pragma: no_include <nlohmann/detail/iterators/iteration_proxy.hpp>
34
35#endif // DOXYGEN_SHOULD_SKIP_THIS
36
37namespace iot::mqtt::server::broker {
38
39 Broker::Broker(uint8_t maxQoS)
40 : sessionStoreFileName((getenv("MQTT_SESSION_STORE") != nullptr) ? getenv("MQTT_SESSION_STORE") : "") // NOLINT
41 , maxQoS(maxQoS)
42 , subscribtionTree(this)
43 , retainTree(this) {
44 if (!sessionStoreFileName.empty()) {
45 std::ifstream sessionStoreFile(sessionStoreFileName);
46
47 if (sessionStoreFile.is_open()) {
48 try {
49 nlohmann::json sessionStoreJson;
50
51 sessionStoreFile >> sessionStoreJson;
52
53 for (const auto& [clientId, sessionJson] : sessionStoreJson["session_store"].items()) {
54 sessionStore[clientId].fromJson(sessionJson);
55 }
56 retainTree.fromJson(sessionStoreJson["retain_tree"]);
57 subscribtionTree.fromJson(sessionStoreJson["subscribtion_tree"]);
58
59 LOG(INFO) << "MQTT Broker: Persistent session data loaded successful";
60 } catch (const nlohmann::json::exception&) {
61 LOG(INFO) << "MQTT Broker: Starting with empty session: Session store '" << sessionStoreFileName
62 << "' empty or corrupted";
63
64 sessionStore.clear();
65 retainTree.clear();
66 subscribtionTree.clear();
67 }
68
69 sessionStoreFile.close();
70 std::remove(sessionStoreFileName.data()); // NOLINT
71
72 LOG(INFO) << "MQTT Broker: Restoring saved session done";
73 } else {
74 PLOG(WARNING) << "MQTT Broker: Could not read session store '" << sessionStoreFileName << "'";
75 }
76 } else {
77 LOG(INFO) << "MQTT Broker: Session not reloaded: Session store filename empty";
78 }
79 }
80
82 if (!sessionStoreFileName.empty()) {
83 nlohmann::json sessionStoreJson;
84
85 for (auto& [clientId, session] : sessionStore) {
86 sessionStoreJson["session_store"][clientId] = session.toJson();
87 }
88 sessionStoreJson["retain_tree"] = retainTree.toJson();
89 sessionStoreJson["subscribtion_tree"] = subscribtionTree.toJson();
90
91 if (sessionStoreJson["session_store"].empty()) {
92 sessionStoreJson.erase("session_store");
93 }
94 if (sessionStoreJson["retain_tree"].empty()) {
95 sessionStoreJson.erase("retain_tree");
96 }
97 if (sessionStoreJson["subscribtion_tree"].empty()) {
98 sessionStoreJson.erase("subscribtion_tree");
99 }
100
101 std::ofstream sessionStoreFile(sessionStoreFileName);
102
103 if (sessionStoreFile.is_open()) {
104 if (!sessionStoreJson.empty()) {
105 sessionStoreFile << sessionStoreJson;
106 }
107
108 sessionStoreFile.close();
109
110 LOG(INFO) << "MQTT Broker: Session store written '" << sessionStoreFileName << "'";
111 } else {
112 PLOG(ERROR) << "MQTT Broker: Could not write session store '" << sessionStoreFileName << "'";
113 }
114 } else {
115 LOG(INFO) << "MQTT Broker: Session not saved: Session store filename empty";
116 }
117 }
118
119 std::shared_ptr<Broker> Broker::instance(uint8_t maxQoS) {
120 static const std::shared_ptr<Broker> broker = std::make_shared<Broker>(maxQoS);
121
122 return broker;
123 }
124
125 void Broker::appear(const std::string& clientId, const std::string& topic, uint8_t qoS) {
126 retainTree.appear(clientId, topic, qoS);
127 }
128
129 void Broker::unsubscribe(const std::string& clientId) {
130 subscribtionTree.unsubscribe(clientId);
131 }
132
133 void
134 Broker::publish(const std::string& originClientId, const std::string& topic, const std::string& message, uint8_t qoS, bool retain) {
135 subscribtionTree.publish(Message(originClientId, topic, message, qoS, retain));
136
137 if (retain) {
138 retainTree.retain(Message(originClientId, topic, message, qoS, retain));
139 }
140 }
141
142 uint8_t Broker::subscribe(const std::string& clientId, const std::string& topic, uint8_t qoS) {
143 qoS = std::min(maxQoS, qoS);
144 uint8_t returnCode = 0;
145
146 if (subscribtionTree.subscribe(topic, clientId, qoS)) {
147 retainTree.appear(clientId, topic, qoS);
148
149 returnCode = SUBSCRIBTION_SUCCESS | qoS;
150 } else {
151 returnCode = SUBSCRIBTION_FAILURE;
152 }
153
154 return returnCode;
155 }
156
157 void Broker::unsubscribe(const std::string& clientId, const std::string& topic) {
158 subscribtionTree.unsubscribe(topic, clientId);
159 }
160
161 bool Broker::hasSession(const std::string& clientId) {
162 return sessionStore.contains(clientId);
163 }
164
165 bool Broker::hasActiveSession(const std::string& clientId) {
166 return hasSession(clientId) && sessionStore[clientId].isActive();
167 }
168
169 bool Broker::hasRetainedSession(const std::string& clientId) {
170 return hasSession(clientId) && !sessionStore[clientId].isActive();
171 }
172
173 bool Broker::isActiveSession(const std::string& clientId, const iot::mqtt::server::Mqtt* mqtt) {
174 return hasSession(clientId) && sessionStore[clientId].isOwnedBy(mqtt);
175 }
176
177 Session* Broker::newSession(const std::string& clientId, iot::mqtt::server::Mqtt* mqtt) {
178 sessionStore[clientId] = iot::mqtt::server::broker::Session(mqtt);
179
180 return &sessionStore[clientId];
181 }
182
183 Session* Broker::renewSession(const std::string& clientId, iot::mqtt::server::Mqtt* mqtt) {
184 return sessionStore[clientId].renew(mqtt);
185 }
186
187 void Broker::restartSession(const std::string& clientId) {
188 LOG(INFO) << "MQTT Broker: Retained: Send PUBLISH: " << clientId;
189 subscribtionTree.appear(clientId);
190
191 LOG(INFO) << "MQTT Broker: Queued: Send PUBLISH: " << clientId;
192 sessionStore[clientId].publishQueued();
193 }
194
195 void Broker::retainSession(const std::string& clientId) {
196 sessionStore[clientId].retain();
197 }
198
199 void Broker::deleteSession(const std::string& clientId) {
200 subscribtionTree.unsubscribe(clientId);
201 sessionStore.erase(clientId);
202 }
203
204 void Broker::sendPublish(const std::string& clientId, Message& message, uint8_t qoS, bool retain) {
205 LOG(INFO) << "MQTT Broker: Send PUBLISH: " << clientId;
206
207 sessionStore[clientId].sendPublish(message, qoS, retain);
208 }
209
210} // namespace iot::mqtt::server::broker
#define SUBSCRIBTION_FAILURE
Definition Broker.h:43
#define SUBSCRIBTION_SUCCESS
Definition Broker.h:42
void restartSession(const std::string &clientId)
Definition Broker.cpp:187
uint8_t subscribe(const std::string &clientId, const std::string &topic, uint8_t qoS)
Definition Broker.cpp:142
bool hasActiveSession(const std::string &clientId)
Definition Broker.cpp:165
static std::shared_ptr< Broker > instance(uint8_t maxQoS)
Definition Broker.cpp:119
bool isActiveSession(const std::string &clientId, const Mqtt *mqtt)
Definition Broker.cpp:173
void publish(const std::string &originClientId, const std::string &topic, const std::string &message, uint8_t qoS, bool retain)
Definition Broker.cpp:134
bool hasSession(const std::string &clientId)
Definition Broker.cpp:161
void appear(const std::string &clientId, const std::string &topic, uint8_t qoS)
Definition Broker.cpp:125
void unsubscribe(const std::string &clientId, const std::string &topic)
Definition Broker.cpp:157
Session * renewSession(const std::string &clientId, iot::mqtt::server::Mqtt *mqtt)
Definition Broker.cpp:183
void deleteSession(const std::string &clientId)
Definition Broker.cpp:199
void unsubscribe(const std::string &clientId)
Definition Broker.cpp:129
bool hasRetainedSession(const std::string &clientId)
Definition Broker.cpp:169
Session * newSession(const std::string &clientId, iot::mqtt::server::Mqtt *mqtt)
Definition Broker.cpp:177
void sendPublish(const std::string &clientId, Message &message, uint8_t qoS, bool retain)
Definition Broker.cpp:204
void retainSession(const std::string &clientId)
Definition Broker.cpp:195