MQTTSuite
Loading...
Searching...
No Matches
mqttbroker.cpp File Reference
#include "SocketContextFactory.h"
#include "config.h"
#include "lib/Mqtt.h"
#include "lib/MqttModel.h"
#include "lib/inja.hpp"
Include dependency graph for mqttbroker.cpp:

Go to the source code of this file.

Functions

static void upgrade APPLICATION (req, res)
static std::string href (const std::string &text, const std::string &link)
static std::string href (const std::string &text, const std::string &url, const std::string &windowId, uint16_t width, uint16_t height)
static std::string getOverviewPage (inja::Environment environment, std::shared_ptr< iot::mqtt::server::broker::Broker > broker, mqtt::mqttbroker::lib::MqttModel &mqttModel)
static std::string getDetailedPage (inja::Environment environment, const mqtt::mqttbroker::lib::Mqtt *mqtt)
static std::string getRedirectSpinnerPage (inja::Environment environment, const std::string &location)
static std::string urlDecode (const std::string &encoded)
static express::Router getRouter (const inja::Environment &environment, std::shared_ptr< iot::mqtt::server::broker::Broker > broker)
static void reportState (const std::string &instanceName, const core::socket::SocketAddress &socketAddress, const core::socket::State &state)
int main (int argc, char *argv[])

Function Documentation

◆ APPLICATION()

void upgrade APPLICATION ( req ,
res  )
static

Definition at line 135 of file mqttbroker.cpp.

135 {
136 const std::string connectionName = res->getSocketContext()->getSocketConnection()->getConnectionName();
137
138 VLOG(2) << connectionName << " HTTP: Upgrade request:\n"
139 << httputils::toString(req->method,
140 req->url,
141 "HTTP/" + std::to_string(req->httpMajor) + "." + std::to_string(req->httpMinor),
142 req->queries,
143 req->headers,
144 req->trailer,
145 req->cookies,
146 std::vector<char>());
147
148 if (req->get("sec-websocket-protocol").find("mqtt") != std::string::npos) {
149 res->upgrade(req, [req, res, connectionName](const std::string& name) {
150 if (!name.empty()) {
151 VLOG(1) << connectionName << ": Successful upgrade:";
152 VLOG(1) << connectionName << ": Selected: " << name;
153 VLOG(1) << connectionName << ": Requested: " << req->get("sec-websocket-protocol");
154
155 res->end();
156 } else {
157 VLOG(1) << connectionName << ": Can not upgrade to any of '" << req->get("upgrade") << "'";
158
159 res->sendStatus(404);
160 }
161 });
162 } else {
163 VLOG(1) << connectionName << ": Unsupported subprotocol(s):";
164 VLOG(1) << " Expected: mqtt";
165 VLOG(1) << " Requested: " << req->get("sec-websocket-protocol");
166
167 res->sendStatus(404);
168 }
169}

◆ getDetailedPage()

std::string getDetailedPage ( inja::Environment environment,
const mqtt::mqttbroker::lib::Mqtt * mqtt )
static

Definition at line 255 of file mqttbroker.cpp.

255 {
256 return environment.render_file("DetailPage.html",
257 {{"table-id", "detail"},
258 {"client_id", mqtt->getClientId()},
259 {"title", mqtt->getClientId()},
260 {"header_row", {"Attribute", "Value"}},
261 {"data_rows",
262 inja::json::array({{"Client ID", mqtt->getClientId()},
263 {"Connection", mqtt->getConnectionName()},
264 {"Clean Session", mqtt->getCleanSession() ? "true" : "false"},
265 {"Connect Flags", std::to_string(mqtt->getConnectFlags())},
266 {"Username", mqtt->getUsername()},
267 {"Username Flag", mqtt->getUsernameFlag() ? "true" : "false"},
268 {"Password", mqtt->getPassword()},
269 {"Password Flag", mqtt->getPasswordFlag() ? "true" : "false"},
270 {"Keep Alive", std::to_string(mqtt->getKeepAlive())},
271 {"Protocol", mqtt->getProtocol()},
272 {"Protocol Level", std::to_string(mqtt->getLevel())},
273 {"Loop Prevention", !mqtt->getReflect() ? "true" : "false"},
274 {"Will Message", mqtt->getWillMessage()},
275 {"Will Topic", mqtt->getWillTopic()},
276 {"Will QoS", std::to_string(mqtt->getWillQoS())},
277 {"Will Flag", mqtt->getWillFlag() ? "true" : "false"},
278 {"Will Retain", mqtt->getWillRetain() ? "true" : "false"}})},
279 {"client_id", mqtt->getClientId()},
280 {"topics", mqtt->getSubscriptions()}});
281}
std::string render_file(const std::string &filename, const json &data)
Definition inja.hpp:2958

Referenced by getRouter().

Here is the caller graph for this function:

◆ getOverviewPage()

std::string getOverviewPage ( inja::Environment environment,
std::shared_ptr< iot::mqtt::server::broker::Broker > broker,
mqtt::mqttbroker::lib::MqttModel & mqttModel )
static

Definition at line 195 of file mqttbroker.cpp.

197 {
199
200 json["table-id"] = "overview";
201 json["title"] = "MQTTBroker | Active Clients";
202 json["voc"] = href("Volker Christian", "https://github.com/VolkerChristian/");
203 json["broker"] = href("MQTTBroker", "https://github.com/SNodeC/mqttsuite/tree/master/mqttbroker");
204 json["suite"] = href("MQTTSuite", "https://github.com/SNodeC/mqttsuite");
205 json["snodec"] = "Powered by " + href("SNode.C", "https://github.com/SNodeC/snode.c");
206 json["since"] = mqttModel.onlineSince();
207 json["duration"] = mqttModel.onlineDuration();
208 json["header_row"] = {"Client ID", "Online Since", "Duration", "Connection", "Local Address", "Remote Address", "Action"};
209 json["data_rows"] = inja::json::array();
210 json["subscribed_topics"] = inja::json::array();
211 json["retained_topics"] = inja::json::array();
212
213 const std::map<std::string, std::list<std::pair<std::string, uint8_t>>>& subscribedTopics = broker->getSubscriptionTree();
214
215 inja::json& subscribedTopicsJson = json["subscribed_topics"];
216 for (const auto& [topic, clients] : subscribedTopics) {
217 inja::json topicJson;
218
219 topicJson["key"] = topic;
220 for (const auto& client : clients) {
221 std::ostringstream windowId("window");
222 for (char ch : client.first) {
223 if (std::isalnum(static_cast<unsigned char>(ch))) {
224 windowId << ch;
225 } else {
226 windowId << std::hex << std::uppercase << std::setw(2) << std::setfill('0')
227 << static_cast<int>(static_cast<unsigned char>(ch));
228 }
229 }
230
231 topicJson["values"].push_back({{"client_id", client.first},
232 {"link", href(client.first, "/client?" + client.first, windowId.str(), 450, 900)},
233 {"topic", topic},
234 {"qos", std::to_string(static_cast<int>(client.second))}});
235 }
236
237 subscribedTopicsJson.push_back(topicJson);
238 }
239
240 std::list<std::pair<std::string, std::string>> retainTree = broker->getRetainTree();
241
242 inja::json& retainedTopicsJson = json["retained_topics"];
243 for (const auto& [topic, message] : retainTree) {
244 inja::json topicJson;
245
246 topicJson["key"] = topic;
247 topicJson["values"].push_back(message);
248
249 retainedTopicsJson.push_back(topicJson);
250 }
251
252 return environment.render_file("OverviewPage.html", json);
253}
nlohmann::json json
static std::string href(const std::string &text, const std::string &link)
nlohmann::json json

References mqtt::mqttbroker::lib::MqttModel::onlineDuration(), and mqtt::mqttbroker::lib::MqttModel::onlineSince().

Referenced by getRouter().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getRedirectSpinnerPage()

std::string getRedirectSpinnerPage ( inja::Environment environment,
const std::string & location )
static

Definition at line 283 of file mqttbroker.cpp.

283 {
284 return environment.render_file("Spinner.html", {{"location", location}});
285}

◆ getRouter()

express::Router getRouter ( const inja::Environment & environment,
std::shared_ptr< iot::mqtt::server::broker::Broker > broker )
static

Definition at line 320 of file mqttbroker.cpp.

320 {
321 const express::Router& jsonRouter = express::middleware::JsonMiddleware();
322
323 jsonRouter.post("/disconnect", [] APPLICATION(req, res) { // cppcheck-suppress unknownMacro
324 VLOG(1) << "POST /disconnect";
325
326 req->getAttribute<nlohmann::json>(
327 [&res](nlohmann::json& json) {
328 std::string jsonString = json.dump(4);
329
330 VLOG(1) << jsonString;
331
332 std::string clientId = json["client_id"].get<std::string>();
334
335 if (mqtt != nullptr) {
336 mqtt->getMqttContext()->getSocketConnection()->close();
337 res->send(jsonString);
338 } else {
339 res->status(404).send("MQTT client has never existed or already gone away: '" + clientId + "'");
340 }
341 },
342 [&res](const std::string& key) {
343 VLOG(1) << "Attribute type not found: " << key;
344
345 res->status(400).send("Attribute type not found: " + key);
346 });
347 });
348
349 jsonRouter.post("/unsubscribe", [] APPLICATION(req, res) {
350 VLOG(1) << "POST /unsubscribe";
351
352 req->getAttribute<nlohmann::json>(
353 [&res](nlohmann::json& json) {
354 std::string jsonString = json.dump(4);
355
356 VLOG(1) << jsonString;
357
358 std::string clientId = json["client_id"].get<std::string>();
359 std::string topic = json["topic"].get<std::string>();
360
362
363 if (mqtt != nullptr) {
364 mqtt->unsubscribe(topic);
365 res->send(jsonString);
366 } else {
367 res->status(404).send("MQTT client has never existed or already gone away: '" + clientId + "'");
368 }
369 },
370 [&res](const std::string& key) {
371 VLOG(1) << "Attribute type not found: " << key;
372
373 res->status(400).send("Attribute type not found: " + key);
374 });
375 });
376
377 jsonRouter.post("/release", [broker] APPLICATION(req, res) {
378 VLOG(1) << "POST /release";
379
380 req->getAttribute<nlohmann::json>(
381 [&res, broker](nlohmann::json& json) {
382 std::string jsonString = json.dump(4);
383
384 VLOG(1) << jsonString;
385
386 std::string topic = json["topic"].get<std::string>();
387
388 broker->release(topic);
389
390 res->send(jsonString);
391 },
392 [&res](const std::string& key) {
393 VLOG(1) << "Attribute type not found: " << key;
394
395 res->status(400).send("Attribute type not found: " + key);
396 });
397 });
398
399 jsonRouter.post("/subscribe", [] APPLICATION(req, res) {
400 VLOG(1) << "POST /subscribe";
401
402 req->getAttribute<nlohmann::json>(
403 [&res](nlohmann::json& json) {
404 std::string jsonString = json.dump(4);
405
406 VLOG(1) << jsonString;
407
408 std::string clientId = json["client_id"].get<std::string>();
409 std::string topic = json["topic"].get<std::string>();
410 uint8_t qoS = json["qos"].get<uint8_t>();
411
413
414 if (mqtt != nullptr) {
415 mqtt->subscribe(topic, qoS);
416 res->send(jsonString);
417 } else {
418 res->status(404).send("MQTT client has never existed or already gone away: '" + clientId + "'");
419 }
420 },
421 [&res](const std::string& key) {
422 VLOG(1) << "Attribute type not found: " << key;
423
424 res->status(400).send("Attribute type not found: " + key);
425 });
426 });
427 const express::Router router;
428
429 router.use(jsonRouter);
430
431 router.get("/clients", [environment, broker] APPLICATION(req, res) {
432 std::string responseString;
433 int responseStatus = 200;
434
435 try {
436 responseString = getOverviewPage(environment, broker, mqtt::mqttbroker::lib::MqttModel::instance());
437 } catch (const inja::InjaError& error) {
438 responseStatus = 500;
439 responseString = "Internal Server Error\n";
440 responseString += std::string(error.what()) + " " + error.type + " " + error.message + " " +
441 std::to_string(error.location.line) + ":" + std::to_string(error.location.column);
442 }
443
444 res->status(responseStatus).send(responseString);
445 });
446
447 router.get("/client", [environment] APPLICATION(req, res) {
448 std::string responseString;
449 int responseStatus = 200;
450
451 if (req->queries.size() == 1) {
453 mqtt::mqttbroker::lib::MqttModel::instance().getMqtt(urlDecode(req->queries.begin()->first));
454
455 if (mqtt != nullptr) {
456 VLOG(1) << "Subscriptions for client " << mqtt->getClientId();
457 for (const std::string& subscription : mqtt->getSubscriptions()) {
458 VLOG(1) << " " << subscription;
459 }
460
461 try {
462 responseString = getDetailedPage(environment, mqtt);
463 } catch (const inja::InjaError& error) {
464 responseStatus = 500;
465 responseString = "Internal Server Error\n";
466 responseString += std::string(error.what()) + " " + error.type + " " + error.message + " " +
467 std::to_string(error.location.line) + ":" + std::to_string(error.location.column);
468 }
469 } else {
470 responseStatus = 404;
471 responseString = "Not Found: " + urlDecode(req->queries.begin()->first);
472 }
473 } else {
474 responseStatus = 400;
475 responseString = "Bad Request: No Client requested";
476 }
477
478 res->status(responseStatus).send(responseString);
479 });
480
481 router.get("/spinner", [environment] APPLICATION(req, res) {
482 std::string responseString;
483 int responseStatus = 200;
484
485 if (req->queries.size() == 1) {
486 responseString = getRedirectSpinnerPage(environment, req->queries.begin()->first);
487 } else {
488 responseStatus = 400;
489 responseString = "Bad Request: No Client requested";
490 }
491 res->status(responseStatus).send(responseString);
492 });
493
494 router.get("/ws", [] APPLICATION(req, res) {
495 if (req->headers.contains("upgrade")) {
496 upgrade(req, res);
497 } else {
498 res->redirect("/spinner?/clients");
499 }
500 });
501
502 router.get("/mqtt", [] APPLICATION(req, res) {
503 if (req->headers.contains("upgrade")) {
504 upgrade(req, res);
505 } else {
506 res->redirect("/spinner?/clients");
507 }
508 });
509
510 router.get("/", [] APPLICATION(req, res) {
511 if (req->headers.contains("upgrade")) {
512 upgrade(req, res);
513 } else {
514 res->redirect("/spinner?/clients");
515 }
516 });
517
518 router.get("/sse", [] APPLICATION(req, res) {
519 if (web::http::ciContains(req->get("Accept"), "text/event-stream")) {
520 res->set("Content-Type", "text/event-stream") //
521 .set("Cache-Control", "no-cache")
522 .set("Connection", "keep-alive");
523 res->sendHeader();
524
525 mqtt::mqttbroker::lib::MqttModel::instance().addEventReceiver(res, req->get("Last-Event-ID"));
526 } else {
527 res->redirect("/spinner?/clients");
528 }
529 });
530
531 return router;
532}
static MqttModel & instance()
Definition MqttModel.cpp:76
void addEventReceiver(const std::shared_ptr< express::Response > &response, const std::string &lastEventId)
const Mqtt * getMqtt(const std::string &clientId)
static std::string getDetailedPage(inja::Environment environment, const mqtt::mqttbroker::lib::Mqtt *mqtt)
static std::string urlDecode(const std::string &encoded)
static void upgrade APPLICATION(req, res)
static std::string getOverviewPage(inja::Environment environment, std::shared_ptr< iot::mqtt::server::broker::Broker > broker, mqtt::mqttbroker::lib::MqttModel &mqttModel)
static std::string getRedirectSpinnerPage(inja::Environment environment, const std::string &location)
const std::string type
Definition inja.hpp:256
const SourceLocation location
Definition inja.hpp:259
const std::string message
Definition inja.hpp:257

References mqtt::mqttbroker::lib::MqttModel::addEventReceiver(), inja::SourceLocation::column, getDetailedPage(), mqtt::mqttbroker::lib::MqttModel::getMqtt(), getOverviewPage(), mqtt::mqttbroker::lib::MqttModel::instance(), inja::SourceLocation::line, inja::InjaError::location, inja::InjaError::message, and inja::InjaError::type.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ href() [1/2]

std::string href ( const std::string & text,
const std::string & link )
static

Definition at line 171 of file mqttbroker.cpp.

171 {
172 return "<a href=\"" + link + "\" style=\"color:inherit;\">" + text + "</a>";
173}

◆ href() [2/2]

std::string href ( const std::string & text,
const std::string & url,
const std::string & windowId,
uint16_t width,
uint16_t height )
static

Definition at line 175 of file mqttbroker.cpp.

175 {
176 return "<a href=\"#\" onClick=\""
177 "let key = '" +
178 windowId +
179 "'; "
180 "if (!localStorage.getItem(key)) "
181 " localStorage.setItem(key, key + '-' + Math.random().toString(36).substr(2, 6)); "
182 "let uniqueId = localStorage.getItem(key); "
183 "if (!window._openWindows) window._openWindows = {}; "
184 "if (!window._openWindows[uniqueId] || window._openWindows[uniqueId].closed) { "
185 " window._openWindows[uniqueId] = window.open('" +
186 url + "', uniqueId, 'width=" + std::to_string(width) + ", height=" + std::to_string(height) +
187 ",location=no, menubar=no, status=no, toolbar=no'); "
188 "} else { "
189 " window._openWindows[uniqueId].focus(); "
190 "} return false;\" "
191 "style=\"color:inherit;\">" +
192 text + "</a>";
193}

◆ main()

int main ( int argc,
char * argv[] )

Definition at line 552 of file mqttbroker.cpp.

552 {
553 utils::Config::addStringOption("--mqtt-mapping-file", "MQTT mapping file (json format) for integration", "[path]", "");
554 utils::Config::addStringOption("--mqtt-session-store", "Path to file for the persistent session store", "[path]", "");
555 utils::Config::addStringOption(
556 "--html-dir", "Path to html source directory", "[path]", std::string(CMAKE_INSTALL_PREFIX) + "/var/www/mqttsuite/mqttbroker");
557
558 core::SNodeC::init(argc, argv);
559
560 std::shared_ptr<iot::mqtt::server::broker::Broker> broker =
561 iot::mqtt::server::broker::Broker::instance(SUBSCRIPTION_MAX_QOS, utils::Config::getStringOptionValue("--mqtt-session-store"));
562
563#ifdef CONFIG_MQTTSUITE_BROKER_TCP_IPV4
564 net::in::stream::legacy::Server<mqtt::mqttbroker::SocketContextFactory>(
565 "in-mqtt",
566 [](auto& config) {
567 config.setPort(1883);
568 config.setRetry();
569 config.setDisableNagleAlgorithm();
570 },
571 broker)
572 .listen([](const auto& socketAddress, core::socket::State state) {
573 reportState("in-mqtt", socketAddress, state);
574 });
575
576#ifdef CONFIG_MQTTSUITE_BROKER_TLS_IPV4
577 net::in::stream::tls::Server<mqtt::mqttbroker::SocketContextFactory>(
578 "in-mqtts",
579 [](auto& config) {
580 config.setPort(8883);
581 config.setRetry();
582 config.setDisableNagleAlgorithm();
583 },
584 broker)
585 .listen([](const auto& socketAddress, core::socket::State state) {
586 reportState("in-mqtts", socketAddress, state);
587 });
588#endif
589#endif
590
591#ifdef CONFIG_MQTTSUITE_BROKER_TCP_IPV6
592 net::in6::stream::legacy::Server<mqtt::mqttbroker::SocketContextFactory>(
593 "in6-mqtt",
594 [](auto& config) {
595 config.setPort(1883);
596 config.setRetry();
597 config.setDisableNagleAlgorithm();
598
599 config.setIPv6Only();
600 },
601 broker)
602 .listen([](const auto& socketAddress, core::socket::State state) {
603 reportState("in6-mqtt", socketAddress, state);
604 });
605
606#ifdef CONFIG_MQTTSUITE_BROKER_TLS_IPV6
607 net::in6::stream::tls::Server<mqtt::mqttbroker::SocketContextFactory>(
608 "in6-mqtts",
609 [](auto& config) {
610 config.setPort(8883);
611 config.setRetry();
612 config.setDisableNagleAlgorithm();
613
614 config.setIPv6Only();
615 },
616 broker)
617 .listen([](const auto& socketAddress, core::socket::State state) {
618 reportState("in6-mqtts", socketAddress, state);
619 });
620#endif
621#endif
622
623#ifdef CONFIG_MQTTSUITE_BROKER_UNIX
624 net::un::stream::legacy::Server<mqtt::mqttbroker::SocketContextFactory>(
625 "un-mqtt",
626 [](auto& config) {
627 config.setSunPath("/tmp/" + utils::Config::getApplicationName() + "-" + config.getInstanceName());
628 config.setRetry();
629 },
630 broker)
631 .listen([](const auto& socketAddress, core::socket::State state) {
632 reportState("un-mqtt", socketAddress, state);
633 });
634
635#ifdef CONFIG_MQTTSUITE_BROKER_UNIX_TLS
636 net::un::stream::tls::Server<mqtt::mqttbroker::SocketContextFactory>(
637 "un-mqtts",
638 [](auto& config) {
639 config.setSunPath("/tmp/" + utils::Config::getApplicationName() + "-" + config.getInstanceName());
640 config.setRetry();
641 },
642 broker)
643 .listen([](const auto& socketAddress, core::socket::State state) {
644 reportState("un-mqtts", socketAddress, state);
645 });
646#endif
647#endif
648
649 inja::Environment environment{utils::Config::getStringOptionValue("--html-dir") + "/"};
650 express::Router router = getRouter(environment, broker);
651
652#ifdef CONFIG_MQTTSUITE_BROKER_TCP_IPV4
653 express::legacy::in::Server("in-http", router, reportState, [](auto& config) {
654 config.setPort(8080);
655 config.setRetry();
656 config.setDisableNagleAlgorithm();
657 });
658
659#ifdef CONFIG_MQTTSUITE_BROKER_TLS_IPV4
660 express::tls::in::Server("in-https", router, reportState, [](auto& config) {
661 config.setPort(8088);
662 config.setRetry();
663 config.setDisableNagleAlgorithm();
664 });
665#endif
666#endif
667
668#ifdef CONFIG_MQTTSUITE_BROKER_TCP_IPV6
669 express::legacy::in6::Server("in6-http", router, reportState, [](auto& config) {
670 config.setPort(8080);
671 config.setRetry();
672 config.setDisableNagleAlgorithm();
673
674 config.setIPv6Only();
675 });
676
677#ifdef CONFIG_MQTTSUITE_BROKER_TLS_IPV6
678 express::tls::in6::Server("in6-https", router, reportState, [](auto& config) {
679 config.setPort(8088);
680 config.setRetry();
681 config.setDisableNagleAlgorithm();
682
683 config.setIPv6Only();
684 });
685#endif
686#endif
687
688#ifdef CONFIG_MQTTSUITE_BROKER_UNIX
689 express::legacy::un::Server("un-http", router, reportState, [](auto& config) {
690 config.setSunPath("/tmp/" + utils::Config::getApplicationName() + "-" + config.getInstanceName());
691 });
692
693#ifdef CONFIG_MQTTSUITE_BROKER_UNIX_TLS
694 express::tls::un::Server("un-https", router, reportState, [](auto& config) {
695 config.setSunPath("/tmp/" + utils::Config::getApplicationName() + "-" + config.getInstanceName());
696 });
697#endif
698#endif
699
700 return core::SNodeC::start();
701}
Class for changing the configuration.
Definition inja.hpp:2851
static void reportState(const std::string &instanceName, const core::socket::SocketAddress &socketAddress, const core::socket::State &state)
static express::Router getRouter(const inja::Environment &environment, std::shared_ptr< iot::mqtt::server::broker::Broker > broker)
static void reportState(const std::string &instanceName, const core::socket::SocketAddress &socketAddress, const core::socket::State &state)

References getRouter().

Here is the call graph for this function:

◆ reportState()

void reportState ( const std::string & instanceName,
const core::socket::SocketAddress & socketAddress,
const core::socket::State & state )
static

Definition at line 535 of file mqttbroker.cpp.

535 {
536 switch (state) {
537 case core::socket::State::OK:
538 VLOG(1) << instanceName << ": listening on '" << socketAddress.toString() << "'";
539 break;
540 case core::socket::State::DISABLED:
541 VLOG(1) << instanceName << ": disabled";
542 break;
543 case core::socket::State::ERROR:
544 VLOG(1) << instanceName << ": " << socketAddress.toString() << ": " << state.what();
545 break;
546 case core::socket::State::FATAL:
547 VLOG(1) << instanceName << ": " << socketAddress.toString() << ": " << state.what();
548 break;
549 }
550}

◆ urlDecode()

std::string urlDecode ( const std::string & encoded)
static

Definition at line 287 of file mqttbroker.cpp.

287 {
288 std::string decoded;
289 size_t i = 0;
290
291 while (i < encoded.length()) {
292 char ch = encoded[i];
293 if (ch == '%') {
294 // Make sure there are at least two characters after '%'
295 if (i + 2 < encoded.length() && std::isxdigit(encoded[i + 1]) && std::isxdigit(encoded[i + 2])) {
296 // Convert the two hex digits to a character
297 std::string hexValue = encoded.substr(i + 1, 2);
298 char decodedChar = static_cast<char>(std::stoi(hexValue, nullptr, 16));
299 decoded.push_back(decodedChar);
300 i += 3; // Skip over the % and the two hex digits
301 } else {
302 // Malformed encoding, just add the '%' as is.
303 decoded.push_back(ch);
304 ++i;
305 }
306 } else if (ch == '+') {
307 // Convert '+' to space (common in URL encoding)
308 decoded.push_back(' ');
309 ++i;
310 } else {
311 // Regular character, just append it.
312 decoded.push_back(ch);
313 ++i;
314 }
315 }
316
317 return decoded;
318}