mirror of https://github.com/nymea/nymea.git
prepared tcp server and mock server for webserver
add TransportInterface add basic structure of webserverpull/135/head
parent
df99a2f438
commit
f1dd14527e
3
guh.pro
3
guh.pro
|
|
@ -5,7 +5,10 @@ TEMPLATE=subdirs
|
|||
SUBDIRS += libguh server plugins
|
||||
|
||||
!disabletesting {
|
||||
message("Building guh tests enabled")
|
||||
SUBDIRS += tests
|
||||
} else {
|
||||
message("Building guh tests disabled")
|
||||
}
|
||||
|
||||
server.depends = libguh plugins
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ Q_LOGGING_CATEGORY(dcDeviceManager, "DeviceManager")
|
|||
Q_LOGGING_CATEGORY(dcRuleEngine, "RuleEngine")
|
||||
Q_LOGGING_CATEGORY(dcHardware, "Hardware")
|
||||
Q_LOGGING_CATEGORY(dcConnection, "Connection")
|
||||
Q_LOGGING_CATEGORY(dcTcpServer, "TcpServer")
|
||||
Q_LOGGING_CATEGORY(dcWebServer, "WebServer")
|
||||
Q_LOGGING_CATEGORY(dcJsonRpc, "JsonRpc")
|
||||
Q_LOGGING_CATEGORY(dcLogEngine, "LogEngine")
|
||||
|
||||
|
|
|
|||
|
|
@ -31,5 +31,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcHardware)
|
|||
Q_DECLARE_LOGGING_CATEGORY(dcConnection)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcJsonRpc)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcLogEngine)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcTcpServer)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcWebServer)
|
||||
|
||||
#endif // LOGGINGCATEGORYS_H
|
||||
|
|
|
|||
|
|
@ -524,6 +524,11 @@ LogEngine* GuhCore::logEngine() const
|
|||
return m_logger;
|
||||
}
|
||||
|
||||
JsonRPCServer *GuhCore::jsonRPCServer() const
|
||||
{
|
||||
return m_jsonServer;
|
||||
}
|
||||
|
||||
void GuhCore::actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status)
|
||||
{
|
||||
emit actionExecuted(id, status);
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ public:
|
|||
RuleEngine::RuleError disableRule(const RuleId &ruleId);
|
||||
|
||||
LogEngine* logEngine() const;
|
||||
JsonRPCServer *jsonRPCServer() const;
|
||||
|
||||
signals:
|
||||
void eventTriggered(const Event &event);
|
||||
|
|
@ -115,7 +116,8 @@ signals:
|
|||
|
||||
private:
|
||||
RuleEngine *ruleEngine() const;
|
||||
DeviceManager* deviceManager() const;
|
||||
DeviceManager *deviceManager() const;
|
||||
|
||||
explicit GuhCore(QObject *parent = 0);
|
||||
static GuhCore *s_instance;
|
||||
RunningMode m_runningMode;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ JsonRPCServer::JsonRPCServer(QObject *parent):
|
|||
// Now set up the logic
|
||||
connect(m_tcpServer, SIGNAL(clientConnected(const QUuid &)), this, SLOT(clientConnected(const QUuid &)));
|
||||
connect(m_tcpServer, SIGNAL(clientDisconnected(const QUuid &)), this, SLOT(clientDisconnected(const QUuid &)));
|
||||
connect(m_tcpServer, SIGNAL(dataAvailable(const QUuid &, QByteArray)), this, SLOT(processData(const QUuid &, QByteArray)));
|
||||
connect(m_tcpServer, SIGNAL(dataAvailable(QUuid, QString, QString, QVariantMap)), this, SLOT(processData(QUuid, QString, QString, QVariantMap)));
|
||||
m_tcpServer->startServer();
|
||||
|
||||
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
|
||||
|
|
@ -138,6 +138,11 @@ JsonReply* JsonRPCServer::SetNotificationStatus(const QVariantMap ¶ms)
|
|||
return createReply(returns);
|
||||
}
|
||||
|
||||
QHash<QString, JsonHandler *> JsonRPCServer::handlers() const
|
||||
{
|
||||
return m_handlers;
|
||||
}
|
||||
|
||||
void JsonRPCServer::setup()
|
||||
{
|
||||
registerHandler(this);
|
||||
|
|
@ -149,50 +154,19 @@ void JsonRPCServer::setup()
|
|||
registerHandler(new StateHandler(this));
|
||||
}
|
||||
|
||||
void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &jsonData)
|
||||
void JsonRPCServer::processData(const QUuid &clientId, const QString &targetNamespace, const QString &method, const QVariantMap &message)
|
||||
{
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
|
||||
// Note: id, targetNamespace and method already checked in TcpServer
|
||||
|
||||
if(error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcJsonRpc) << "failed to parse data" << jsonData << ":" << error.errorString();
|
||||
}
|
||||
|
||||
QVariantMap message = jsonDoc.toVariant().toMap();
|
||||
|
||||
bool success;
|
||||
int commandId = message.value("id").toInt(&success);
|
||||
if (!success) {
|
||||
qCWarning(dcJsonRpc) << "Error parsing command. Missing \"id\":" << jsonData;
|
||||
sendErrorResponse(clientId, commandId, "Error parsing command. Missing 'id'");
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList commandList = message.value("method").toString().split('.');
|
||||
if (commandList.count() != 2) {
|
||||
qCWarning(dcJsonRpc) << "Error parsing method.\nGot:" << message.value("method").toString() << "\nExpected: \"Namespace.method\"";
|
||||
sendErrorResponse(clientId, commandId, QString("Error parsing method. Got: '%1'', Expected: 'Namespace.method'").arg(message.value("method").toString()));
|
||||
return;
|
||||
}
|
||||
|
||||
QString targetNamespace = commandList.first();
|
||||
QString method = commandList.last();
|
||||
int commandId = message.value("id").toInt();
|
||||
QVariantMap params = message.value("params").toMap();
|
||||
|
||||
emit commandReceived(targetNamespace, method, params);
|
||||
|
||||
JsonHandler *handler = m_handlers.value(targetNamespace);
|
||||
if (!handler) {
|
||||
sendErrorResponse(clientId, commandId, "No such namespace");
|
||||
return;
|
||||
}
|
||||
if (!handler->hasMethod(method)) {
|
||||
sendErrorResponse(clientId, commandId, "No such method");
|
||||
return;
|
||||
}
|
||||
JsonHandler *handler = m_handlers.value(targetNamespace);
|
||||
QPair<bool, QString> validationResult = handler->validateParams(method, params);
|
||||
if (!validationResult.first) {
|
||||
sendErrorResponse(clientId, commandId, "Invalid params: " + validationResult.second);
|
||||
m_tcpServer->sendErrorResponse(clientId, commandId, "Invalid params: " + validationResult.second);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +183,7 @@ void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &jsonDat
|
|||
} else {
|
||||
Q_ASSERT_X((targetNamespace == "JSONRPC" && method == "Introspect") || handler->validateReturns(method, reply->data()).first
|
||||
,"validating return value", formatAssertion(targetNamespace, method, handler, reply->data()).toLatin1().data());
|
||||
sendResponse(clientId, commandId, reply->data());
|
||||
m_tcpServer->sendResponse(clientId, commandId, reply->data());
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
|
|
@ -234,8 +208,7 @@ void JsonRPCServer::sendNotification(const QVariantMap ¶ms)
|
|||
notification.insert("notification", handler->name() + "." + method.name());
|
||||
notification.insert("params", params);
|
||||
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(notification);
|
||||
m_tcpServer->sendData(m_clients.keys(true), jsonDoc.toJson());
|
||||
m_tcpServer->sendData(m_clients.keys(true), notification);
|
||||
}
|
||||
|
||||
void JsonRPCServer::asyncReplyFinished()
|
||||
|
|
@ -244,10 +217,11 @@ void JsonRPCServer::asyncReplyFinished()
|
|||
if (!reply->timedOut()) {
|
||||
Q_ASSERT_X(reply->handler()->validateReturns(reply->method(), reply->data()).first
|
||||
,"validating return value", formatAssertion(reply->handler()->name(), reply->method(), reply->handler(), reply->data()).toLatin1().data());
|
||||
sendResponse(reply->clientId(), reply->commandId(), reply->data());
|
||||
m_tcpServer->sendResponse(reply->clientId(), reply->commandId(), reply->data());
|
||||
} else {
|
||||
sendErrorResponse(reply->clientId(), reply->commandId(), "Command timed out");
|
||||
m_tcpServer->sendErrorResponse(reply->clientId(), reply->commandId(), "Command timed out");
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
|
|
@ -272,8 +246,7 @@ void JsonRPCServer::clientConnected(const QUuid &clientId)
|
|||
handshake.insert("server", "guh JSONRPC interface");
|
||||
handshake.insert("version", GUH_VERSION_STRING);
|
||||
handshake.insert("protocol version", JSON_PROTOCOL_VERSION);
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(handshake);
|
||||
m_tcpServer->sendData(clientId, jsonDoc.toJson());
|
||||
m_tcpServer->sendData(clientId, handshake);
|
||||
}
|
||||
|
||||
void JsonRPCServer::clientDisconnected(const QUuid &clientId)
|
||||
|
|
@ -281,26 +254,4 @@ void JsonRPCServer::clientDisconnected(const QUuid &clientId)
|
|||
m_clients.remove(clientId);
|
||||
}
|
||||
|
||||
void JsonRPCServer::sendResponse(const QUuid &clientId, int commandId, const QVariantMap ¶ms)
|
||||
{
|
||||
QVariantMap rsp;
|
||||
rsp.insert("id", commandId);
|
||||
rsp.insert("status", "success");
|
||||
rsp.insert("params", params);
|
||||
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(rsp);
|
||||
m_tcpServer->sendData(clientId, jsonDoc.toJson());
|
||||
}
|
||||
|
||||
void JsonRPCServer::sendErrorResponse(const QUuid &clientId, int commandId, const QString &error)
|
||||
{
|
||||
QVariantMap rsp;
|
||||
rsp.insert("id", commandId);
|
||||
rsp.insert("status", "error");
|
||||
rsp.insert("error", error);
|
||||
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(rsp);
|
||||
m_tcpServer->sendData(clientId, jsonDoc.toJson());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ public:
|
|||
Q_INVOKABLE JsonReply* Version(const QVariantMap ¶ms) const;
|
||||
Q_INVOKABLE JsonReply* SetNotificationStatus(const QVariantMap ¶ms);
|
||||
|
||||
QHash<QString, JsonHandler*> handlers() const;
|
||||
|
||||
signals:
|
||||
void commandReceived(const QString &targetNamespace, const QString &command, const QVariantMap ¶ms);
|
||||
|
||||
|
|
@ -63,7 +65,7 @@ private slots:
|
|||
void clientConnected(const QUuid &clientId);
|
||||
void clientDisconnected(const QUuid &clientId);
|
||||
|
||||
void processData(const QUuid &clientId, const QByteArray &jsonData);
|
||||
void processData(const QUuid &clientId, const QString &targetNamespace, const QString &method, const QVariantMap &message);
|
||||
|
||||
void sendNotification(const QVariantMap ¶ms);
|
||||
|
||||
|
|
@ -72,9 +74,6 @@ private slots:
|
|||
private:
|
||||
void registerHandler(JsonHandler *handler);
|
||||
|
||||
void sendResponse(const QUuid &clientId, int commandId, const QVariantMap ¶ms = QVariantMap());
|
||||
void sendErrorResponse(const QUuid &clientId, int commandId, const QString &error);
|
||||
|
||||
QString formatAssertion(const QString &targetNamespace, const QString &method, JsonHandler *handler, const QVariantMap &data) const;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ int main(int argc, char *argv[])
|
|||
s_loggingFilters.insert("DeviceManager", true);
|
||||
s_loggingFilters.insert("RuleEngine", true);
|
||||
s_loggingFilters.insert("Connection", true);
|
||||
s_loggingFilters.insert("TcpServer", true);
|
||||
s_loggingFilters.insert("WebServer", true);
|
||||
s_loggingFilters.insert("JsonRpc", false);
|
||||
s_loggingFilters.insert("Hardware", false);
|
||||
s_loggingFilters.insert("LogEngine", false);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ SOURCES += $$top_srcdir/server/guhcore.cpp \
|
|||
$$top_srcdir/server/logging/logengine.cpp \
|
||||
$$top_srcdir/server/logging/logfilter.cpp \
|
||||
$$top_srcdir/server/logging/logentry.cpp \
|
||||
$$top_srcdir/server/webserver.cpp \
|
||||
$$top_srcdir/server/transportinterface.cpp \
|
||||
|
||||
|
||||
HEADERS += $$top_srcdir/server/guhcore.h \
|
||||
$$top_srcdir/server/tcpserver.h \
|
||||
|
|
@ -35,3 +38,6 @@ HEADERS += $$top_srcdir/server/guhcore.h \
|
|||
$$top_srcdir/server/logging/logengine.h \
|
||||
$$top_srcdir/server/logging/logfilter.h \
|
||||
$$top_srcdir/server/logging/logentry.h \
|
||||
$$top_srcdir/server/webserver.h \
|
||||
$$top_srcdir/server/transportinterface.h \
|
||||
|
||||
|
|
|
|||
|
|
@ -28,4 +28,4 @@ boblight {
|
|||
}
|
||||
|
||||
HEADERS += \
|
||||
guhservice.h
|
||||
guhservice.h \
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
#include "tcpserver.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "guhsettings.h"
|
||||
#include "guhcore.h"
|
||||
#include "jsonrpcserver.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
|
|
@ -29,7 +31,7 @@
|
|||
namespace guhserver {
|
||||
|
||||
TcpServer::TcpServer(QObject *parent) :
|
||||
QObject(parent)
|
||||
TransportInterface(parent)
|
||||
{
|
||||
// Timer for scanning network interfaces ever 5 seconds
|
||||
// Note: QNetworkConfigurationManager does not work on embedded platforms
|
||||
|
|
@ -40,7 +42,7 @@ TcpServer::TcpServer(QObject *parent) :
|
|||
|
||||
// load JSON-RPC server settings
|
||||
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
|
||||
qCDebug(dcConnection) << "Loading TCP server settings from:" << settings.fileName();
|
||||
qCDebug(dcTcpServer) << "Loading Tcp server settings from:" << settings.fileName();
|
||||
settings.beginGroup("JSONRPC");
|
||||
|
||||
// load port
|
||||
|
|
@ -63,7 +65,7 @@ TcpServer::TcpServer(QObject *parent) :
|
|||
settings.endGroup();
|
||||
}
|
||||
|
||||
void TcpServer::sendData(const QList<QUuid> &clients, const QByteArray &data)
|
||||
void TcpServer::sendData(const QList<QUuid> &clients, const QVariantMap &data)
|
||||
{
|
||||
foreach (const QUuid &client, clients) {
|
||||
sendData(client, data);
|
||||
|
|
@ -92,11 +94,76 @@ void TcpServer::reloadNetworkInterfaces()
|
|||
}
|
||||
|
||||
|
||||
void TcpServer::sendData(const QUuid &clientId, const QByteArray &data)
|
||||
void TcpServer::validateMessage(const QUuid &clientId, const QByteArray &data)
|
||||
{
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
|
||||
if(error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcJsonRpc) << "Failed to parse JSON data" << data << ":" << error.errorString();
|
||||
sendErrorResponse(clientId, -1, QString("Failed to parse JSON data: %1").arg(error.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap message = jsonDoc.toVariant().toMap();
|
||||
|
||||
bool success;
|
||||
int commandId = message.value("id").toInt(&success);
|
||||
if (!success) {
|
||||
qCWarning(dcJsonRpc) << "Error parsing command. Missing \"id\":" << message;
|
||||
sendErrorResponse(clientId, commandId, "Error parsing command. Missing 'id'");
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList commandList = message.value("method").toString().split('.');
|
||||
if (commandList.count() != 2) {
|
||||
qCWarning(dcJsonRpc) << "Error parsing method.\nGot:" << message.value("method").toString() << "\nExpected: \"Namespace.method\"";
|
||||
sendErrorResponse(clientId, commandId, QString("Error parsing method. Got: '%1'', Expected: 'Namespace.method'").arg(message.value("method").toString()));
|
||||
return;
|
||||
}
|
||||
|
||||
QString targetNamespace = commandList.first();
|
||||
QString method = commandList.last();
|
||||
|
||||
JsonHandler *handler = GuhCore::instance()->jsonRPCServer()->handlers().value(targetNamespace);
|
||||
if (!handler) {
|
||||
sendErrorResponse(clientId, commandId, "No such namespace");
|
||||
return;
|
||||
}
|
||||
if (!handler->hasMethod(method)) {
|
||||
sendErrorResponse(clientId, commandId, "No such method");
|
||||
return;
|
||||
}
|
||||
|
||||
emit dataAvailable(clientId, targetNamespace, method, message);
|
||||
}
|
||||
|
||||
void TcpServer::sendResponse(const QUuid &clientId, int commandId, const QVariantMap ¶ms)
|
||||
{
|
||||
QVariantMap response;
|
||||
response.insert("id", commandId);
|
||||
response.insert("status", "success");
|
||||
response.insert("params", params);
|
||||
|
||||
sendData(clientId, response);
|
||||
}
|
||||
|
||||
void TcpServer::sendErrorResponse(const QUuid &clientId, int commandId, const QString &error)
|
||||
{
|
||||
QVariantMap errorResponse;
|
||||
errorResponse.insert("id", commandId);
|
||||
errorResponse.insert("status", "error");
|
||||
errorResponse.insert("error", error);
|
||||
|
||||
sendData(clientId, errorResponse);
|
||||
}
|
||||
|
||||
|
||||
void TcpServer::sendData(const QUuid &clientId, const QVariantMap &data)
|
||||
{
|
||||
QTcpSocket *client = m_clientList.value(clientId);
|
||||
if (client) {
|
||||
client->write(data);
|
||||
client->write(QJsonDocument::fromVariant(data).toJson());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +172,7 @@ void TcpServer::newClientConnected()
|
|||
// got a new client connected
|
||||
QTcpServer *server = qobject_cast<QTcpServer*>(sender());
|
||||
QTcpSocket *newConnection = server->nextPendingConnection();
|
||||
qCDebug(dcConnection) << "new client connected:" << newConnection->peerAddress().toString();
|
||||
qCDebug(dcConnection) << "Tcp server: new client connected:" << newConnection->peerAddress().toString();
|
||||
|
||||
QUuid clientId = QUuid::createUuid();
|
||||
|
||||
|
|
@ -122,14 +189,14 @@ void TcpServer::newClientConnected()
|
|||
void TcpServer::readPackage()
|
||||
{
|
||||
QTcpSocket *client = qobject_cast<QTcpSocket*>(sender());
|
||||
qCDebug(dcConnection) << "data comming from" << client->peerAddress().toString();
|
||||
qCDebug(dcTcpServer) << "data comming from" << client->peerAddress().toString();
|
||||
QByteArray message;
|
||||
while (client->canReadLine()) {
|
||||
QByteArray dataLine = client->readLine();
|
||||
qCDebug(dcConnection) << "line in:" << dataLine;
|
||||
qCDebug(dcTcpServer) << "line in:" << dataLine;
|
||||
message.append(dataLine);
|
||||
if (dataLine.endsWith('\n')) {
|
||||
emit dataAvailable(m_clientList.key(client), message);
|
||||
validateMessage(m_clientList.key(client), message);
|
||||
message.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -138,7 +205,7 @@ void TcpServer::readPackage()
|
|||
void TcpServer::onClientDisconnected()
|
||||
{
|
||||
QTcpSocket *client = qobject_cast<QTcpSocket*>(sender());
|
||||
qCDebug(dcConnection) << "client disconnected:" << client->peerAddress().toString();
|
||||
qCDebug(dcConnection) << "Tcp server: client disconnected:" << client->peerAddress().toString();
|
||||
QUuid clientId = m_clientList.key(client);
|
||||
m_clientList.take(clientId)->deleteLater();
|
||||
}
|
||||
|
|
@ -147,8 +214,8 @@ void TcpServer::onError(const QAbstractSocket::SocketError &error)
|
|||
{
|
||||
QTcpServer *server = qobject_cast<QTcpServer *>(sender());
|
||||
QUuid uuid = m_serverList.key(server);
|
||||
qCWarning(dcConnection) << "TCP server" << server->serverAddress().toString() << "error:" << error << server->errorString();
|
||||
qCWarning(dcConnection) << "shutting down TCP server" << server->serverAddress().toString();
|
||||
qCWarning(dcTcpServer) << "Tcp server" << server->serverAddress().toString() << "error:" << error << server->errorString();
|
||||
qCWarning(dcTcpServer) << "shutting down Tcp server" << server->serverAddress().toString();
|
||||
|
||||
server->close();
|
||||
m_serverList.take(uuid)->deleteLater();
|
||||
|
|
@ -220,7 +287,7 @@ void TcpServer::onTimeout()
|
|||
}
|
||||
|
||||
if (!serversToCreate.isEmpty() || !serversToDelete.isEmpty()) {
|
||||
qCDebug(dcConnection) << "current server list:";
|
||||
qCDebug(dcConnection) << "Current server list:";
|
||||
foreach (QTcpServer *s, m_serverList) {
|
||||
qCDebug(dcConnection) << " - Tcp server on" << s->serverAddress().toString() << s->serverPort();
|
||||
}
|
||||
|
|
@ -230,7 +297,6 @@ void TcpServer::onTimeout()
|
|||
|
||||
bool TcpServer::startServer()
|
||||
{
|
||||
|
||||
bool ipV4 = m_ipVersions.contains("IPv4");
|
||||
bool ipV6 = m_ipVersions.contains("IPv6");
|
||||
if (m_ipVersions.contains("any")) {
|
||||
|
|
|
|||
|
|
@ -29,16 +29,18 @@
|
|||
#include <QUuid>
|
||||
#include <QTimer>
|
||||
|
||||
#include "transportinterface.h"
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
class TcpServer : public QObject
|
||||
class TcpServer : public TransportInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TcpServer(QObject *parent = 0);
|
||||
|
||||
void sendData(const QUuid &clientId, const QByteArray &data);
|
||||
void sendData(const QList<QUuid> &clients, const QByteArray &data);
|
||||
void sendData(const QUuid &clientId, const QVariantMap &data);
|
||||
void sendData(const QList<QUuid> &clients, const QVariantMap &data);
|
||||
|
||||
private:
|
||||
QTimer *m_timer;
|
||||
|
|
@ -51,12 +53,12 @@ private:
|
|||
QStringList m_ipVersions;
|
||||
|
||||
void reloadNetworkInterfaces();
|
||||
void validateMessage(const QUuid &clientId, const QByteArray &data);
|
||||
|
||||
public:
|
||||
void sendResponse(const QUuid &clientId, int commandId, const QVariantMap ¶ms = QVariantMap());
|
||||
void sendErrorResponse(const QUuid &clientId, int commandId, const QString &error);
|
||||
|
||||
signals:
|
||||
void clientConnected(const QUuid &clientId);
|
||||
void clientDisconnected(const QUuid &clientId);
|
||||
void dataAvailable(const QUuid &clientId, const QByteArray &data);
|
||||
|
||||
private slots:
|
||||
void newClientConnected();
|
||||
void readPackage();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
|
||||
* *
|
||||
* This file is part of guh. *
|
||||
* *
|
||||
* Guh is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, version 2 of the License. *
|
||||
* *
|
||||
* Guh is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "transportinterface.h"
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
TransportInterface::TransportInterface(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
|
||||
* *
|
||||
* This file is part of guh. *
|
||||
* *
|
||||
* Guh is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, version 2 of the License. *
|
||||
* *
|
||||
* Guh is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef TRANSPORTINTERFACE_H
|
||||
#define TRANSPORTINTERFACE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
class TransportInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TransportInterface(QObject *parent = 0);
|
||||
virtual void sendData(const QUuid &clientId, const QVariantMap &data) = 0;
|
||||
virtual void sendData(const QList<QUuid> &clients, const QVariantMap &data) = 0;
|
||||
|
||||
signals:
|
||||
void clientConnected(const QUuid &clientId);
|
||||
void clientDisconnected(const QUuid &clientId);
|
||||
void dataAvailable(const QUuid &clientId, const QString &targetNamespace, const QString &method, const QVariantMap &message);
|
||||
|
||||
public slots:
|
||||
virtual bool startServer() = 0;
|
||||
virtual bool stopServer() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TRANSPORTINTERFACE_H
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
|
||||
* *
|
||||
* This file is part of guh. *
|
||||
* *
|
||||
* Guh is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, version 2 of the License. *
|
||||
* *
|
||||
* Guh is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "webserver.h"
|
||||
#include "loggingcategories.h"
|
||||
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QUuid>
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
WebServer::WebServer(QObject *parent) :
|
||||
TransportInterface(parent),
|
||||
m_enabled(false)
|
||||
{
|
||||
m_server = new QTcpServer(this);
|
||||
|
||||
connect(m_server, &QTcpServer::newConnection, this, &WebServer::onNewConnection);
|
||||
}
|
||||
|
||||
WebServer::~WebServer()
|
||||
{
|
||||
m_server->close();
|
||||
}
|
||||
|
||||
void WebServer::sendData(const QUuid &clientId, const QByteArray &data)
|
||||
{
|
||||
Q_UNUSED(clientId)
|
||||
Q_UNUSED(data)
|
||||
|
||||
|
||||
}
|
||||
|
||||
void WebServer::sendData(const QList<QUuid> &clients, const QByteArray &data)
|
||||
{
|
||||
Q_UNUSED(clients)
|
||||
Q_UNUSED(data)
|
||||
}
|
||||
|
||||
QString WebServer::createContentHeader()
|
||||
{
|
||||
QString contentHeader(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: application/json; charset=\"utf-8\"\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
return contentHeader;
|
||||
}
|
||||
|
||||
void WebServer::onNewConnection()
|
||||
{
|
||||
QTcpSocket* socket = m_server->nextPendingConnection();
|
||||
connect(socket, &QTcpSocket::readyRead, this, &WebServer::readClient);
|
||||
connect(socket, &QTcpSocket::disconnected, this, &WebServer::discardClient);
|
||||
qCDebug(dcConnection) << "Webserver client connected" << socket->peerName() << socket->peerAddress().toString() << socket->peerPort();
|
||||
}
|
||||
|
||||
void WebServer::readClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WebServer::discardClient()
|
||||
{
|
||||
QTcpSocket* socket = static_cast<QTcpSocket *>(sender());
|
||||
qCDebug(dcConnection) << "Webserver client disonnected" << socket->peerName() << socket->peerAddress().toString() << socket->peerPort();
|
||||
|
||||
}
|
||||
|
||||
bool WebServer::startServer()
|
||||
{
|
||||
if (!m_server->listen(QHostAddress::Any, m_port)) {
|
||||
qCWarning(dcConnection) << "Webserver could not listen on" << m_server->serverAddress().toString() << m_port;
|
||||
m_enabled = false;
|
||||
return false;
|
||||
}
|
||||
qCDebug(dcConnection) << "Started webserver on" << m_server->serverAddress().toString() << m_port;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebServer::stopServer()
|
||||
{
|
||||
m_server->close();
|
||||
qCDebug(dcConnection) << "Webserver closed.";
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
|
||||
* *
|
||||
* This file is part of guh. *
|
||||
* *
|
||||
* Guh is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, version 2 of the License. *
|
||||
* *
|
||||
* Guh is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef WEBSERVER_H
|
||||
#define WEBSERVER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
|
||||
#include "transportinterface.h"
|
||||
|
||||
class QTcpServer;
|
||||
class QTcpSocket;
|
||||
class QUuid;
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
class WebServer : public TransportInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WebServer(QObject *parent = 0);
|
||||
~WebServer();
|
||||
void sendData(const QUuid &clientId, const QByteArray &data);
|
||||
void sendData(const QList<QUuid> &clients, const QByteArray &data);
|
||||
|
||||
private:
|
||||
QTcpServer *m_server;
|
||||
QHash<QUuid, QTcpSocket *> m_clientList;
|
||||
|
||||
bool m_enabled;
|
||||
qint16 m_port;
|
||||
|
||||
QString createContentHeader();
|
||||
|
||||
signals:
|
||||
|
||||
private slots:
|
||||
void onNewConnection();
|
||||
void readClient();
|
||||
void discardClient();
|
||||
|
||||
public slots:
|
||||
bool startServer();
|
||||
bool stopServer();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WEBSERVER_H
|
||||
|
|
@ -19,15 +19,21 @@
|
|||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "mocktcpserver.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "jsonrpcserver.h"
|
||||
#include "guhcore.h"
|
||||
|
||||
#include <QUuid>
|
||||
#include <QHash>
|
||||
#include <QJsonDocument>
|
||||
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
QList<MockTcpServer*> MockTcpServer::s_allServers;
|
||||
|
||||
MockTcpServer::MockTcpServer(QObject *parent):
|
||||
QObject(parent)
|
||||
TransportInterface(parent)
|
||||
{
|
||||
s_allServers.append(this);
|
||||
}
|
||||
|
|
@ -37,12 +43,12 @@ MockTcpServer::~MockTcpServer()
|
|||
s_allServers.removeAll(this);
|
||||
}
|
||||
|
||||
void MockTcpServer::sendData(const QUuid &clientId, const QByteArray &data)
|
||||
void MockTcpServer::sendData(const QUuid &clientId, const QVariantMap &data)
|
||||
{
|
||||
emit outgoingData(clientId, data);
|
||||
emit outgoingData(clientId, QJsonDocument::fromVariant(data).toJson());
|
||||
}
|
||||
|
||||
void MockTcpServer::sendData(const QList<QUuid> &clients, const QByteArray &data)
|
||||
void MockTcpServer::sendData(const QList<QUuid> &clients, const QVariantMap &data)
|
||||
{
|
||||
foreach (const QUuid &clientId, clients) {
|
||||
sendData(clientId, data);
|
||||
|
|
@ -56,7 +62,67 @@ QList<MockTcpServer *> MockTcpServer::servers()
|
|||
|
||||
void MockTcpServer::injectData(const QUuid &clientId, const QByteArray &data)
|
||||
{
|
||||
emit dataAvailable(clientId, data);
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
|
||||
if(error.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcJsonRpc) << "Failed to parse JSON data" << data << ":" << error.errorString();
|
||||
sendErrorResponse(clientId, -1, QString("Failed to parse JSON data: %1").arg(error.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap message = jsonDoc.toVariant().toMap();
|
||||
|
||||
bool success;
|
||||
int commandId = message.value("id").toInt(&success);
|
||||
if (!success) {
|
||||
qCWarning(dcJsonRpc) << "Error parsing command. Missing \"id\":" << message;
|
||||
sendErrorResponse(clientId, commandId, "Error parsing command. Missing 'id'");
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList commandList = message.value("method").toString().split('.');
|
||||
if (commandList.count() != 2) {
|
||||
qCWarning(dcJsonRpc) << "Error parsing method.\nGot:" << message.value("method").toString() << "\nExpected: \"Namespace.method\"";
|
||||
sendErrorResponse(clientId, commandId, QString("Error parsing method. Got: '%1'', Expected: 'Namespace.method'").arg(message.value("method").toString()));
|
||||
return;
|
||||
}
|
||||
|
||||
QString targetNamespace = commandList.first();
|
||||
QString method = commandList.last();
|
||||
|
||||
JsonHandler *handler = GuhCore::instance()->jsonRPCServer()->handlers().value(targetNamespace);
|
||||
if (!handler) {
|
||||
sendErrorResponse(clientId, commandId, "No such namespace");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handler->hasMethod(method)) {
|
||||
sendErrorResponse(clientId, commandId, "No such method");
|
||||
return;
|
||||
}
|
||||
|
||||
emit dataAvailable(clientId, targetNamespace, method, message);
|
||||
}
|
||||
|
||||
void MockTcpServer::sendResponse(const QUuid &clientId, int commandId, const QVariantMap ¶ms)
|
||||
{
|
||||
QVariantMap response;
|
||||
response.insert("id", commandId);
|
||||
response.insert("status", "success");
|
||||
response.insert("params", params);
|
||||
|
||||
sendData(clientId, response);
|
||||
}
|
||||
|
||||
void MockTcpServer::sendErrorResponse(const QUuid &clientId, int commandId, const QString &error)
|
||||
{
|
||||
QVariantMap errorResponse;
|
||||
errorResponse.insert("id", commandId);
|
||||
errorResponse.insert("status", "error");
|
||||
errorResponse.insert("error", error);
|
||||
|
||||
sendData(clientId, errorResponse);
|
||||
}
|
||||
|
||||
bool MockTcpServer::startServer()
|
||||
|
|
|
|||
|
|
@ -25,17 +25,21 @@
|
|||
#include <QNetworkInterface>
|
||||
#include <QDebug>
|
||||
|
||||
#include "transportinterface.h"
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
class MockTcpServer : public QObject
|
||||
class JsonRPCServer;
|
||||
|
||||
class MockTcpServer : public TransportInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MockTcpServer(QObject *parent = 0);
|
||||
~MockTcpServer();
|
||||
|
||||
void sendData(const QUuid &clientId, const QByteArray &data);
|
||||
void sendData(const QList<QUuid> &clients, const QByteArray &data);
|
||||
void sendData(const QUuid &clientId, const QVariantMap &data);
|
||||
void sendData(const QList<QUuid> &clients, const QVariantMap &data);
|
||||
|
||||
/************** Used for testing **************************/
|
||||
static QList<MockTcpServer*> servers();
|
||||
|
|
@ -44,10 +48,9 @@ signals:
|
|||
void outgoingData(const QUuid &clientId, const QByteArray &data);
|
||||
/************** Used for testing **************************/
|
||||
|
||||
signals:
|
||||
void clientConnected(const QUuid &clientId);
|
||||
void clientDisconnected(const QUuid &clientId);
|
||||
void dataAvailable(const QUuid &clientId, const QByteArray &data);
|
||||
public:
|
||||
void sendResponse(const QUuid &clientId, int commandId, const QVariantMap ¶ms = QVariantMap());
|
||||
void sendErrorResponse(const QUuid &clientId, int commandId, const QString &error);
|
||||
|
||||
public slots:
|
||||
bool startServer();
|
||||
|
|
|
|||
Loading…
Reference in New Issue