MQTTSuite
Loading...
Searching...
No Matches
mqttstore.cpp
Go to the documentation of this file.
1/*
2 * MQTTSuite - A lightweight MQTT Integration System
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2022, 2023, 2024, 2025, 2026
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3 of the License, or (at your option)
9 * any later version.
10 */
11
13#include "config.h"
14#include "lib/ConfigSections.h"
15
16#ifdef LINK_SUBPROTOCOL_STATIC
17
18#include "websocket/SubProtocolFactory.h"
19
20#include <web/websocket/client/SubProtocolFactorySelector.h>
21
22#endif
23
24#if defined(LINK_WEBSOCKET_STATIC) || defined(LINK_SUBPROTOCOL_STATIC)
25
26#include <web/websocket/client/SocketContextUpgradeFactory.h>
27
28#endif
29
30#include <core/SNodeC.h>
31#include <net/config/ConfigInstance.h>
32//
33#include <net/in/stream/legacy/SocketClient.h>
34#include <net/in/stream/tls/SocketClient.h>
35#include <net/in6/stream/legacy/SocketClient.h>
36#include <net/in6/stream/tls/SocketClient.h>
37#include <net/un/stream/legacy/SocketClient.h>
38#include <net/un/stream/tls/SocketClient.h>
39#include <web/http/client/ConfigHTTP.h>
40#include <web/http/http_utils.h>
41#include <web/http/legacy/in/Client.h>
42#include <web/http/legacy/in6/Client.h>
43#include <web/http/legacy/un/Client.h>
44#include <web/http/tls/in/Client.h>
45#include <web/http/tls/in6/Client.h>
46#include <web/http/tls/un/Client.h>
47
48#ifndef DOXYGEN_SHOULD_SKIP_THIS
49
50#include <cstdlib>
51#include <exception>
52#include <iostream>
53#include <log/Logger.h>
54
55#endif
56
57static void
58reportState(const std::string& instanceName, const core::socket::SocketAddress& socketAddress, const core::socket::State& state) {
59 switch (state) {
60 case core::socket::State::OK:
61 VLOG(1) << instanceName << ": connected to '" << socketAddress.toString() << "'";
62 break;
63 case core::socket::State::DISABLED:
64 VLOG(1) << instanceName << ": disabled";
65 break;
66 case core::socket::State::ERROR:
67 VLOG(1) << instanceName << ": " << socketAddress.toString() << ": " << state.what();
68 break;
69 case core::socket::State::FATAL:
70 VLOG(1) << instanceName << ": " << socketAddress.toString() << ": " << state.what();
71 break;
72 }
73}
74
75static void logResponse(const std::shared_ptr<web::http::client::Request>& req, const std::shared_ptr<web::http::client::Response>& res) {
76 VLOG(1) << req->getConnectionName() << " HTTP response for: " << req->method << " " << req->url << " HTTP/" << req->httpMajor << "."
77 << req->httpMinor << "\n"
78 << httputils::toString(req->method,
79 req->url,
80 "HTTP/" + std::to_string(req->httpMajor) + "." + std::to_string(req->httpMinor),
81 req->getQueries(),
82 req->getHeaders(),
83 req->getTrailer(),
84 req->getCookies(),
85 {})
86 << "\n"
87 << httputils::toString(res->httpVersion, res->statusCode, res->reason, res->headers, res->cookies, res->body);
88}
89
90template <template <typename SocketContextFactoryT, typename... ArgsT> typename SocketClient>
91static SocketClient<mqtt::mqttstore::SocketContextFactory>
92startClient(const std::string& instanceName,
93 const std::function<void(typename SocketClient<mqtt::mqttstore::SocketContextFactory>::Config*)>& configurator) {
94 using Client = SocketClient<mqtt::mqttstore::SocketContextFactory>;
95 using SocketAddress = typename Client::SocketAddress;
96
97 Client socketClient = core::socket::stream::Client<Client>(instanceName, configurator);
98
99 socketClient.getConfig()->setRetry();
100 socketClient.getConfig()->setRetryBase(1);
101 socketClient.getConfig()->setReconnect();
102 socketClient.getConfig()->setDisabled();
103
104 socketClient.connect([instanceName](const SocketAddress& socketAddress, const core::socket::State& state) {
105 reportState(instanceName, socketAddress, state);
106 });
107
108 return socketClient;
109}
110
111template <typename HttpClient>
112static HttpClient startClient(const std::string& name, const std::function<void(typename HttpClient::Config*)>& configurator) {
113 using SocketAddress = typename HttpClient::SocketAddress;
114
115 const HttpClient httpClient(
116 name,
117 [](const std::shared_ptr<web::http::client::MasterRequest>& req) {
118 const std::string connectionName = req->getSocketContext()->getSocketConnection()->getConnectionName();
119 const std::string target = req->getSocketContext()
120 ->getSocketConnection()
121 ->getConfigInstance()
122 ->getSubCommand<web::http::client::ConfigHTTP>()
123 ->getOption("--target")
124 ->as<std::string>();
125
126 req->set("Sec-WebSocket-Protocol", "mqtt");
127
128 req->upgrade(
129 target,
130 "websocket",
131 [connectionName](bool success) {
132 VLOG(1) << connectionName << ": HTTP Upgrade (http -> websocket||mqtt) start " << (success ? "success" : "failed");
133 },
134 [connectionName](const std::shared_ptr<web::http::client::Request>& req,
135 const std::shared_ptr<web::http::client::Response>& res,
136 bool success) {
137 logResponse(req, res);
138 VLOG(1) << connectionName << ": HTTP Upgrade " << (success ? "success" : "failed");
139 },
140 [connectionName](const std::shared_ptr<web::http::client::Request>& req, const std::string& message) {
141 VLOG(1) << connectionName << ": Response parse error: " << message;
142 VLOG(1) << " Request was: " << req->method << " " << req->url << " HTTP/" << req->httpMajor << "." << req->httpMinor;
143 });
144 },
145 []([[maybe_unused]] const std::shared_ptr<web::http::client::Request>& req) {
146 VLOG(1) << "Session ended";
147 });
148
149 configurator(httpClient.getConfig());
150
151 httpClient.getConfig()->setRetry();
152 httpClient.getConfig()->setRetryBase(1);
153 httpClient.getConfig()->setReconnect();
154 httpClient.getConfig()->setDisabled();
155
156 httpClient.connect([name](const SocketAddress& socketAddress, const core::socket::State& state) {
157 reportState(name, socketAddress, state);
158 });
159
160 return httpClient;
161}
162
163static void createConfig(net::config::ConfigInstance* config) {
164 config //
165 ->newSubCommand<mqtt::mqttstore::lib::ConfigSession>();
166
167 config //
168 ->newSubCommand<mqtt::mqttstore::lib::ConfigSubscribe>();
169
170 config //
171 ->newSubCommand<mqtt::mqttstore::lib::ConfigDatabase>()
172 ->newSubCommand<mqtt::mqttstore::lib::ConfigStorage>();
173}
174
175static void createWSConfig(net::config::ConfigInstance* config) {
176 createConfig(config);
177
178 config->getSubCommand<web::http::client::ConfigHTTP>()
179 ->addOption("--target", "Websocket endpoint", "string", "/ws", CLI::TypeValidator<std::string>())
180 ->configurable();
181}
182
183static int run(int argc, char* argv[]) {
184 core::SNodeC::init(argc, argv);
185
186#ifdef CONFIG_MQTTSUITE_STORE_TCP_IPV4
187 startClient<net::in::stream::legacy::SocketClient>("in-mqtt", [](net::in::stream::legacy::config::ConfigSocketClient* config) {
188 config->Remote::setPort(1883);
189 config->setDisableNagleAlgorithm();
190 createConfig(config);
191 });
192#endif
193
194#ifdef CONFIG_MQTTSUITE_STORE_TLS_IPV4
195 startClient<net::in::stream::tls::SocketClient>("in-mqtts", [](net::in::stream::tls::config::ConfigSocketClient* config) {
196 config->Remote::setPort(1883);
197 config->setDisableNagleAlgorithm();
198 createConfig(config);
199 });
200#endif
201
202#ifdef CONFIG_MQTTSUITE_STORE_TCP_IPV6
203 startClient<net::in6::stream::legacy::SocketClient>("in6-mqtt", [](net::in6::stream::legacy::config::ConfigSocketClient* config) {
204 config->Remote::setPort(1883);
205 config->setDisableNagleAlgorithm();
206 createConfig(config);
207 });
208#endif
209
210#ifdef CONFIG_MQTTSUITE_STORE_TLS_IPV6
211 startClient<net::in6::stream::tls::SocketClient>("in6-mqtts", [](net::in6::stream::tls::config::ConfigSocketClient* config) {
212 config->Remote::setPort(1883);
213 config->setDisableNagleAlgorithm();
214 createConfig(config);
215 });
216#endif
217
218#ifdef CONFIG_MQTTSUITE_STORE_UNIX
219 startClient<net::un::stream::legacy::SocketClient>("un-mqtt", [](net::un::stream::legacy::config::ConfigSocketClient* config) {
220 createConfig(config);
221 });
222#endif
223
224#ifdef CONFIG_MQTTSUITE_STORE_UNIX_TLS
225 startClient<net::un::stream::tls::SocketClient>("un-mqtts", [](net::un::stream::tls::config::ConfigSocketClient* config) {
226 createConfig(config);
227 });
228#endif
229
230#if defined(CONFIG_MQTTSUITE_STORE_TCP_IPV4) && defined(CONFIG_MQTTSUITE_STORE_WS)
231 startClient<web::http::legacy::in::Client>("in-wsmqtt", [](net::in::stream::legacy::config::ConfigSocketClient* config) {
232 config->Remote::setPort(8080);
233 config->setDisableNagleAlgorithm();
234 createWSConfig(config);
235 });
236#endif
237
238#if defined(CONFIG_MQTTSUITE_STORE_TLS_IPV4) && defined(CONFIG_MQTTSUITE_STORE_WSS)
239 startClient<web::http::tls::in::Client>("in-wsmqtts", [](net::in::stream::tls::config::ConfigSocketClient* config) {
240 config->Remote::setPort(8088);
241 config->setDisableNagleAlgorithm();
242 createWSConfig(config);
243 });
244#endif
245
246#if defined(CONFIG_MQTTSUITE_STORE_TCP_IPV6) && defined(CONFIG_MQTTSUITE_STORE_WS)
247 startClient<web::http::legacy::in6::Client>("in6-wsmqtt", [](net::in6::stream::legacy::config::ConfigSocketClient* config) {
248 config->Remote::setPort(8080);
249 config->setDisableNagleAlgorithm();
250 createWSConfig(config);
251 });
252#endif
253
254#if defined(CONFIG_MQTTSUITE_STORE_TLS_IPV6) && defined(CONFIG_MQTTSUITE_STORE_WSS)
255 startClient<web::http::tls::in6::Client>("in6-wsmqtts", [](net::in6::stream::tls::config::ConfigSocketClient* config) {
256 config->Remote::setPort(8088);
257 config->setDisableNagleAlgorithm();
258 createWSConfig(config);
259 });
260#endif
261
262#if defined(CONFIG_MQTTSUITE_STORE_UNIX) && defined(CONFIG_MQTTSUITE_STORE_WS)
263 startClient<web::http::legacy::un::Client>("un-wsmqtt", [](net::un::stream::legacy::config::ConfigSocketClient* config) {
264 createWSConfig(config);
265 });
266#endif
267
268#if defined(CONFIG_MQTTSUITE_STORE_UNIX_TLS) && defined(CONFIG_MQTTSUITE_STORE_WSS)
269 startClient<web::http::tls::un::Client>("un-wsmqtts", [](net::un::stream::tls::config::ConfigSocketClient* config) {
270 createWSConfig(config);
271 });
272#endif
273
274 return core::SNodeC::start();
275}
276
277int main(int argc, char* argv[]) {
278 try {
279 return run(argc, argv);
280 } catch (const std::exception& exception) {
281 std::cerr << "mqttstore: fatal startup error: " << exception.what() << std::endl;
282 return EXIT_FAILURE;
283 } catch (...) {
284 std::cerr << "mqttstore: fatal startup error: unknown exception" << std::endl;
285 return EXIT_FAILURE;
286 }
287}
int main(int argc, char *argv[])
static SocketClient< mqtt::mqttstore::SocketContextFactory > startClient(const std::string &instanceName, const std::function< void(typename SocketClient< mqtt::mqttstore::SocketContextFactory >::Config *)> &configurator)
Definition mqttstore.cpp:92
static HttpClient startClient(const std::string &name, const std::function< void(typename HttpClient::Config *)> &configurator)
static void reportState(const std::string &instanceName, const core::socket::SocketAddress &socketAddress, const core::socket::State &state)
Definition mqttstore.cpp:58
static void logResponse(const std::shared_ptr< web::http::client::Request > &req, const std::shared_ptr< web::http::client::Response > &res)
Definition mqttstore.cpp:75
static int run(int argc, char *argv[])
static void createWSConfig(net::config::ConfigInstance *config)
static void createConfig(net::config::ConfigInstance *config)