2026-01-18 12:53:55 -06:00
|
|
|
/**
|
|
|
|
|
* @file MessageServer.cpp
|
|
|
|
|
* @brief Implementation of the external client message server
|
|
|
|
|
*/
|
|
|
|
|
|
2020-04-03 22:49:28 -04:00
|
|
|
#include "MessageServer.h"
|
2026-01-18 12:53:55 -06:00
|
|
|
|
2025-10-12 03:34:27 +02:00
|
|
|
#include <QLoggingCategory>
|
2026-01-18 12:53:55 -06:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
#include <stdexcept>
|
2026-01-18 12:53:55 -06:00
|
|
|
|
2025-10-12 03:34:27 +02:00
|
|
|
Q_DECLARE_LOGGING_CATEGORY(messageserver_js8)
|
2018-02-08 21:28:33 -05:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
MessageServer::MessageServer(QObject *parent) : QTcpServer(parent) {}
|
2018-02-08 21:28:33 -05:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
MessageServer::~MessageServer() { stop(); }
|
2018-02-08 21:28:33 -05:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
bool MessageServer::start() {
|
|
|
|
|
if (isListening()) {
|
|
|
|
|
qCDebug(messageserver_js8)
|
|
|
|
|
<< "MessageServer already listening:" << m_host << m_port;
|
2020-04-03 22:49:28 -04:00
|
|
|
return false;
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
|
2020-04-03 22:49:28 -04:00
|
|
|
auto address = QHostAddress();
|
2026-01-01 15:12:24 -06:00
|
|
|
if (m_host.isEmpty() || !address.setAddress(m_host)) {
|
|
|
|
|
qCDebug(messageserver_js8)
|
|
|
|
|
<< "MessageServer address invalid:" << m_host << m_port;
|
2020-04-03 22:49:28 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2018-02-08 21:28:33 -05:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
if (m_port <= 0) {
|
|
|
|
|
qCDebug(messageserver_js8)
|
|
|
|
|
<< "MessageServer port invalid:" << m_host << m_port;
|
2020-04-04 14:58:30 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-03 22:49:28 -04:00
|
|
|
bool listening = listen(address, m_port);
|
2026-01-01 15:12:24 -06:00
|
|
|
qCDebug(messageserver_js8)
|
|
|
|
|
<< "MessageServer listening:" << listening << m_host << m_port;
|
2018-02-08 21:28:33 -05:00
|
|
|
|
2020-04-03 22:49:28 -04:00
|
|
|
return listening;
|
|
|
|
|
}
|
2018-02-08 21:28:33 -05:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void MessageServer::stop() {
|
2020-04-03 22:49:28 -04:00
|
|
|
// disconnect all clients
|
2026-01-01 15:12:24 -06:00
|
|
|
foreach (auto client, m_clients) {
|
2020-04-03 22:49:28 -04:00
|
|
|
client->close();
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
2020-04-03 22:49:28 -04:00
|
|
|
|
|
|
|
|
// then close the server
|
|
|
|
|
close();
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void MessageServer::setServer(QString host, quint16 port) {
|
2020-04-03 22:49:28 -04:00
|
|
|
bool listening = isListening();
|
2026-01-01 15:12:24 -06:00
|
|
|
if (listening && (m_host != host || m_port != port)) {
|
2020-04-03 22:49:28 -04:00
|
|
|
stop();
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
|
2020-04-03 22:49:28 -04:00
|
|
|
m_host = host;
|
|
|
|
|
m_port = port;
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
if (listening) {
|
2020-04-03 22:49:28 -04:00
|
|
|
start();
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void MessageServer::setPause(bool paused) {
|
2020-04-03 22:49:28 -04:00
|
|
|
m_paused = paused;
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
if (paused) {
|
2020-04-03 22:49:28 -04:00
|
|
|
pauseAccepting();
|
|
|
|
|
} else {
|
|
|
|
|
resumeAccepting();
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void MessageServer::setMaxConnections(int n) {
|
2020-04-04 14:58:30 -04:00
|
|
|
// set the maximum number of connections allowed
|
|
|
|
|
m_maxConnections = n;
|
|
|
|
|
|
|
|
|
|
// then, prune old ones greater than the max (fifo)
|
|
|
|
|
pruneConnections();
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
int MessageServer::activeConnections() {
|
|
|
|
|
int i = 0;
|
|
|
|
|
foreach (auto client, m_clients) {
|
|
|
|
|
if (client->isConnected())
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
return i;
|
2020-04-04 14:58:30 -04:00
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void MessageServer::pruneConnections() {
|
2020-04-04 14:58:30 -04:00
|
|
|
// keep only the n most recent connections (fifo)
|
2026-01-01 15:12:24 -06:00
|
|
|
if (m_maxConnections && m_maxConnections < activeConnections()) {
|
|
|
|
|
for (int i = m_maxConnections; i < activeConnections(); i++) {
|
2020-04-04 14:58:30 -04:00
|
|
|
auto client = m_clients.first();
|
|
|
|
|
client->close();
|
|
|
|
|
m_clients.removeFirst();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void MessageServer::send(const Message &message) {
|
|
|
|
|
foreach (auto client, m_clients) {
|
|
|
|
|
if (!client->awaitingResponse(message.id())) {
|
2020-04-03 22:49:28 -04:00
|
|
|
continue;
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
2020-04-03 22:49:28 -04:00
|
|
|
client->send(message);
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void MessageServer::incomingConnection(qintptr handle) {
|
2025-10-12 03:34:27 +02:00
|
|
|
qCDebug(messageserver_js8) << "MessageServer incomingConnection" << handle;
|
2020-04-03 22:49:28 -04:00
|
|
|
|
|
|
|
|
auto client = new Client(this, this);
|
|
|
|
|
client->setSocket(handle);
|
|
|
|
|
|
|
|
|
|
#if JS8_MESSAGESERVER_IS_SINGLE_CLIENT
|
2026-01-01 15:12:24 -06:00
|
|
|
while (!m_clients.isEmpty()) {
|
2020-04-03 22:49:28 -04:00
|
|
|
auto client = m_clients.first();
|
|
|
|
|
client->close();
|
|
|
|
|
m_clients.removeFirst();
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
2020-04-03 22:49:28 -04:00
|
|
|
#endif
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
if (m_maxConnections && m_maxConnections <= activeConnections()) {
|
|
|
|
|
qCDebug(messageserver_js8)
|
|
|
|
|
<< "MessageServer connections full, dropping incoming connection";
|
2020-04-04 14:58:30 -04:00
|
|
|
client->send(Message("API.ERROR", "Connections Full"));
|
|
|
|
|
client->close();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-03 22:49:28 -04:00
|
|
|
m_clients.append(client);
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
Client::Client(MessageServer *server, QObject *parent)
|
|
|
|
|
: QObject(parent), m_server{server} {
|
2020-04-03 22:49:28 -04:00
|
|
|
setConnected(true);
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void Client::setSocket(qintptr handle) {
|
2020-04-03 22:49:28 -04:00
|
|
|
m_socket = new QTcpSocket(this);
|
|
|
|
|
|
|
|
|
|
connect(m_socket, &QTcpSocket::disconnected, this, &Client::onDisconnected);
|
|
|
|
|
connect(m_socket, &QTcpSocket::readyRead, this, &Client::readyRead);
|
|
|
|
|
|
|
|
|
|
m_socket->setSocketDescriptor(handle);
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void Client::setConnected(bool connected) { m_connected = connected; }
|
2018-02-08 21:28:33 -05:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void Client::close() {
|
|
|
|
|
if (!m_socket) {
|
2020-04-03 22:49:28 -04:00
|
|
|
return;
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
2020-04-03 22:49:28 -04:00
|
|
|
|
|
|
|
|
m_socket->close();
|
|
|
|
|
m_socket = nullptr;
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void Client::send(const Message &message) {
|
|
|
|
|
if (!isConnected()) {
|
2020-04-03 22:49:28 -04:00
|
|
|
return;
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
if (!m_socket) {
|
2020-04-03 22:49:28 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
if (!m_socket->isOpen()) {
|
2025-10-12 03:34:27 +02:00
|
|
|
qCDebug(messageserver_js8) << "client socket isn't open";
|
2020-04-03 22:49:28 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-12 03:34:27 +02:00
|
|
|
qCDebug(messageserver_js8) << "client writing" << message.toJson();
|
2020-04-03 22:49:28 -04:00
|
|
|
m_socket->write(message.toJson());
|
|
|
|
|
m_socket->write("\n");
|
|
|
|
|
m_socket->flush();
|
|
|
|
|
|
|
|
|
|
// remove if needed
|
2026-01-01 15:12:24 -06:00
|
|
|
if (m_requests.contains(message.id())) {
|
2020-04-03 22:49:28 -04:00
|
|
|
m_requests.remove(message.id());
|
2018-02-08 21:28:33 -05:00
|
|
|
}
|
|
|
|
|
}
|
2018-03-05 14:49:51 -05:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void Client::onDisconnected() {
|
2025-10-12 03:34:27 +02:00
|
|
|
qCDebug(messageserver_js8) << "MessageServer client disconnected";
|
2020-04-03 22:49:28 -04:00
|
|
|
setConnected(false);
|
2018-03-05 14:49:51 -05:00
|
|
|
}
|
2018-08-05 11:33:30 -04:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
void Client::readyRead() {
|
2025-10-12 03:34:27 +02:00
|
|
|
qCDebug(messageserver_js8) << "MessageServer client readyRead";
|
2020-04-03 22:49:28 -04:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
while (m_socket->canReadLine()) {
|
2024-11-03 23:02:32 -08:00
|
|
|
auto const msg = m_socket->readLine().trimmed();
|
2026-01-01 15:12:24 -06:00
|
|
|
qCDebug(messageserver_js8)
|
|
|
|
|
<< "-> Client" << m_socket->socketDescriptor() << msg;
|
2020-04-03 22:49:28 -04:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
if (msg.isEmpty())
|
|
|
|
|
return;
|
2020-04-03 22:49:28 -04:00
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
try {
|
2024-11-03 23:02:32 -08:00
|
|
|
auto m = Message::fromJson(msg);
|
|
|
|
|
m_requests[m.ensureId()] = m;
|
|
|
|
|
emit m_server->message(m);
|
2026-01-01 15:12:24 -06:00
|
|
|
} catch (std::exception const &e) {
|
2024-11-03 23:02:32 -08:00
|
|
|
send({"API.ERROR", e.what()});
|
2020-04-03 22:49:28 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-05 11:33:30 -04:00
|
|
|
}
|
2025-10-12 03:34:27 +02:00
|
|
|
|
|
|
|
|
Q_LOGGING_CATEGORY(messageserver_js8, "messageserver.js8", QtWarningMsg)
|