js8call/JS8_Main/MessageServer.cpp

219 lines
5.2 KiB
C++
Raw Permalink Normal View History

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