/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2015 Simon Stuerz * * * * 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 . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /*! \class guhserver::TransportInterface \brief This class provides an interface for the JSON servers. \ingroup server \inmodule core \sa WebSocketServer, TcpServer */ /*! \fn void guhserver::TransportInterface::clientConnected(const QUuid &clientId); This signal is emitted when a new client with the given \a clientId has been connected. \sa WebSocketServer, TcpServer */ /*! \fn void guhserver::TransportInterface::clientDisconnected(const QUuid &clientId); This signal is emitted when a new client with the given \a clientId has been disconnected. \sa WebSocketServer, TcpServer */ /*! \fn bool guhserver::TransportInterface::startServer(); Pure virtual public slot for starting the corresponding \l{TransportInterface}. Returns true if started successfully. \sa WebSocketServer::startServer(), TcpServer::startServer() */ /*! \fn bool guhserver::TransportInterface::stopServer(); Pure virtual public slot for stopping the corresponding \l{TransportInterface}. Returns true if stopped successfully. \sa WebSocketServer::stopServer(), TcpServer::stopServer() */ /*! \fn void guhserver::TransportInterface::sendData(const QUuid &clientId, const QVariantMap &data); Pure virtual method for sending \a data to the client with the id \a clientId over the corresponding \l{TransportInterface}. */ /*! \fn void guhserver::TransportInterface::sendData(const QList &clients, const QVariantMap &data); Pure virtual method for sending \a data to \a clients over the corresponding \l{TransportInterface}. */ /*! \fn void guhserver::TransportInterface::dataAvailable(const QUuid &clientId, const QString &targetNamespace, const QString &method, const QVariantMap &message); This signal is emitted when valid data from the client with the given \a clientId are available. Data are valid if the corresponding \l{TransportInterface} has parsed successfully the given \a targetNamespace, \a method and \a message. \sa WebSocketServer, TcpServer */ #include "transportinterface.h" #include "loggingcategories.h" #include "jsonhandler.h" #include "guhcore.h" #include namespace guhserver { /*! Constructs a \l{TransportInterface} with the given \a parent. */ TransportInterface::TransportInterface(QObject *parent) : QObject(parent) { } /*! Pure virtual destructor for \l{TransportInterface}. */ TransportInterface::~TransportInterface() { } /*! Send a JSON success response to the client with the given \a clientId, * \a commandId and \a params to the inerted \l{TransportInterface}. */ void TransportInterface::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); } /*! Send a JSON error response to the client with the given \a clientId, * \a commandId and \a error to the inerted \l{TransportInterface}. */ void TransportInterface::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); } /*! Validates the given \a data from the client with the id \a clientId. If the validation was * successfull, the signal \l{dataAvailable()} will be emitted, otherwise an error response * will be sent to the client. * * \sa dataAvailable() */ void TransportInterface::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); } }