#include "Connection.hpp" #include #include #include #include #include #include #include using namespace Epistmool::Server; namespace { constexpr int kInvalidGeneration = 0; } // namespace ConnectionId ConnectionId::createInvalid() { return ConnectionId{ .index = 0, .generation = kInvalidGeneration, }; } bool ConnectionId::isInvalid() const { return generation == kInvalidGeneration; } struct ConnectionManager::Connection { enum Type { Empty, LocalSocket, }; union { QLocalSocket* localSocket; }; Type type = Empty; int generation = kInvalidGeneration + 1; void markEmpty() { type = Empty; } }; class ConnectionManager::Private { public: static int findFreeConnectionObj(ConnectionManager& self) { for (int i = 0; i < self.mConnections.size(); ++i) { auto& conn = self.mConnections[i]; if (conn.type == Connection::Empty) { return i; } } self.mConnections.push_back({}); return self.mConnections.size() - 1; } }; ConnectionManager::ConnectionManager(QObject* parent) : QObject(parent) { } ConnectionManager::~ConnectionManager() = default; bool ConnectionManager::isLocalConnectionsEnabled() const { return mLocal != nullptr; } void ConnectionManager::setLocalConnectionsEnabled(bool enabled) { bool currentlyEnabled = isLocalConnectionsEnabled(); if (!currentlyEnabled && enabled) { mLocal = new QLocalServer(this); if (!mLocal->listen("queue")) { qFatal("Failed to initialized event queue"); } connect(mLocal, &QLocalServer::newConnection, mLocal, [&]() { auto socket = mLocal->nextPendingConnection(); ConnectionId connId; connId.index = Private::findFreeConnectionObj(*this); auto& connObj = mConnections[connId.index]; connId.generation = connObj.generation++; connObj.type = Connection::LocalSocket; connObj.localSocket = socket; connect(socket, &QLocalSocket::disconnected, socket, &QObject::deleteLater); connect(socket, &QLocalSocket::disconnected, this, [this, connId]() { mConnections[connId.index].markEmpty(); }); connect(socket, &QLocalSocket::readyRead, this, [this, socket, connId]() { while (socket->canReadLine()) { auto line = socket->readLine(); QJsonParseError error; auto msg = QJsonDocument::fromJson(line, &error); if (error.error != QJsonParseError::NoError) { qWarning() << "Invalid message from client: " << error.errorString(); qWarning() << "Message: " << QString(line); } else { qDebug() << QString(line); } emit messageRecieved(msg, connId); } }); emit connectionEstablished(connId); }); } else if (currentlyEnabled && !enabled) { mLocal->deleteLater(); mLocal = nullptr; } } void ConnectionManager::replyMessage(ConnectionId id, const QJsonDocument& message) { if (id.index < 0 || id.index >= mConnections.size()) { return; } auto& conn = mConnections[id.index]; if (id.generation != conn.generation) { return; } switch (conn.type) { case Connection::Empty: return; case Connection::LocalSocket: { auto socket = conn.localSocket; socket->write(message.toJson(QJsonDocument::Compact)); socket->flush(); } break; } }