diff options
Diffstat (limited to 'server-v1/source/EpistmoolServer/Connection.cpp')
-rw-r--r-- | server-v1/source/EpistmoolServer/Connection.cpp | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/server-v1/source/EpistmoolServer/Connection.cpp b/server-v1/source/EpistmoolServer/Connection.cpp new file mode 100644 index 0000000..59dcf38 --- /dev/null +++ b/server-v1/source/EpistmoolServer/Connection.cpp @@ -0,0 +1,150 @@ +#include "Connection.hpp" + +#include <QJsonDocument> +#include <QJsonParseError> +#include <QLocalServer> +#include <QLocalSocket> +#include <QString> +#include <QtDebug> +#include <vector> + +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; + } +} |