Implement basic functionality of JSON RPC server
This commit is contained in:
parent
f20173df9f
commit
de6c757a26
@ -46,12 +46,11 @@ In order to run the test, you can call `make check` in the build directory or ru
|
|||||||
If you want to create a line coverage report from the tests simply run following command in the source directory:
|
If you want to create a line coverage report from the tests simply run following command in the source directory:
|
||||||
|
|
||||||
|
|
||||||
$ apt install lcov
|
$ apt install lcov gcovr
|
||||||
$ ./create-coverage-html.sh
|
$ ./create-coverage-html.sh
|
||||||
|
|
||||||
The resulting coverage report will be place in the `coverage-html` directory.
|
The resulting coverage report will be place in the `coverage-html` directory.
|
||||||
|
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
In order to get information about the server you can start the command with the `--help` parameter.
|
In order to get information about the server you can start the command with the `--help` parameter.
|
||||||
|
|||||||
@ -1,22 +1,18 @@
|
|||||||
#include "authenticationreply.h"
|
#include "authenticationreply.h"
|
||||||
#include "authentication/authenticator.h"
|
#include "authentication/authenticator.h"
|
||||||
|
|
||||||
AuthenticationReply::AuthenticationReply(const QUuid clientId, const QString &token, QObject *parent) :
|
namespace remoteproxy {
|
||||||
|
|
||||||
|
AuthenticationReply::AuthenticationReply(ProxyClient *proxyClient, QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
m_clientId(clientId),
|
m_proxyClient(proxyClient)
|
||||||
m_token(token)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QUuid AuthenticationReply::clientId() const
|
ProxyClient *AuthenticationReply::proxyClient() const
|
||||||
{
|
{
|
||||||
return m_clientId;
|
return m_proxyClient;
|
||||||
}
|
|
||||||
|
|
||||||
QString AuthenticationReply::token() const
|
|
||||||
{
|
|
||||||
return m_token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AuthenticationReply::isTimedOut() const
|
bool AuthenticationReply::isTimedOut() const
|
||||||
@ -29,18 +25,32 @@ bool AuthenticationReply::isFinished() const
|
|||||||
return m_finished;
|
return m_finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Authenticator::AuthenticationError AuthenticationReply::error() const
|
||||||
|
{
|
||||||
|
return m_error;
|
||||||
|
}
|
||||||
|
|
||||||
void AuthenticationReply::setError(Authenticator::AuthenticationError error)
|
void AuthenticationReply::setError(Authenticator::AuthenticationError error)
|
||||||
{
|
{
|
||||||
m_error = error;
|
m_error = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AuthenticationReply::setFinished()
|
||||||
|
{
|
||||||
|
emit finished();
|
||||||
|
}
|
||||||
|
|
||||||
void AuthenticationReply::onTimeout()
|
void AuthenticationReply::onTimeout()
|
||||||
{
|
{
|
||||||
|
m_timedOut = true;
|
||||||
|
m_error = Authenticator::AuthenticationErrorTimeout;
|
||||||
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthenticationReply::abort()
|
void AuthenticationReply::abort()
|
||||||
{
|
{
|
||||||
|
m_error = Authenticator::AuthenticationErrorAborted;
|
||||||
|
emit finished();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,16 +8,15 @@
|
|||||||
|
|
||||||
#include "authenticator.h"
|
#include "authenticator.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class AuthenticationReply : public QObject
|
class AuthenticationReply : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
friend class Authenticator;
|
friend class Authenticator;
|
||||||
|
|
||||||
explicit AuthenticationReply(const QUuid clientId, const QString &token, QObject *parent = nullptr);
|
ProxyClient *proxyClient() const;
|
||||||
|
|
||||||
QUuid clientId() const;
|
|
||||||
QString token() const;
|
|
||||||
|
|
||||||
bool isTimedOut() const;
|
bool isTimedOut() const;
|
||||||
bool isFinished() const;
|
bool isFinished() const;
|
||||||
@ -25,15 +24,18 @@ public:
|
|||||||
Authenticator::AuthenticationError error() const;
|
Authenticator::AuthenticationError error() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUuid m_clientId;
|
explicit AuthenticationReply(ProxyClient *proxyClient, QObject *parent = nullptr);
|
||||||
QString m_token;
|
|
||||||
|
ProxyClient *m_proxyClient = nullptr;
|
||||||
QTimer m_timer;
|
QTimer m_timer;
|
||||||
|
|
||||||
bool m_timedOut = false;
|
bool m_timedOut = false;
|
||||||
bool m_finished = false;
|
bool m_finished = false;
|
||||||
Authenticator::AuthenticationError m_error;
|
|
||||||
|
Authenticator::AuthenticationError m_error = Authenticator::AuthenticationErrorUnknown;
|
||||||
|
|
||||||
void setError(Authenticator::AuthenticationError error);
|
void setError(Authenticator::AuthenticationError error);
|
||||||
|
void setFinished();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void finished();
|
void finished();
|
||||||
@ -46,4 +48,6 @@ public slots:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // AUTHENTICATIONREPLY_H
|
#endif // AUTHENTICATIONREPLY_H
|
||||||
|
|||||||
@ -1,13 +1,33 @@
|
|||||||
|
#include "proxyclient.h"
|
||||||
#include "authenticator.h"
|
#include "authenticator.h"
|
||||||
#include "authenticationreply.h"
|
#include "authenticationreply.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
Authenticator::Authenticator(QObject *parent) :
|
Authenticator::Authenticator(QObject *parent) :
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Authenticator::setReplyError(AuthenticationReply *reply, Authenticator::AuthenticationError error)
|
||||||
|
{
|
||||||
|
reply->setError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Authenticator::setReplyFinished(AuthenticationReply *reply)
|
||||||
|
{
|
||||||
|
reply->setFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticationReply *Authenticator::createAuthenticationReply(ProxyClient *proxyClient, QObject *parent)
|
||||||
|
{
|
||||||
|
return new AuthenticationReply(proxyClient, parent);
|
||||||
|
}
|
||||||
|
|
||||||
Authenticator::~Authenticator()
|
Authenticator::~Authenticator()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
|
class ProxyClient;
|
||||||
class AuthenticationReply;
|
class AuthenticationReply;
|
||||||
|
|
||||||
class Authenticator : public QObject
|
class Authenticator : public QObject
|
||||||
@ -12,6 +15,7 @@ class Authenticator : public QObject
|
|||||||
public:
|
public:
|
||||||
enum AuthenticationError {
|
enum AuthenticationError {
|
||||||
AuthenticationErrorNoError,
|
AuthenticationErrorNoError,
|
||||||
|
AuthenticationErrorUnknown,
|
||||||
AuthenticationErrorTimeout,
|
AuthenticationErrorTimeout,
|
||||||
AuthenticationErrorAborted,
|
AuthenticationErrorAborted,
|
||||||
AuthenticationErrorAuthenticationFailed,
|
AuthenticationErrorAuthenticationFailed,
|
||||||
@ -22,8 +26,18 @@ public:
|
|||||||
explicit Authenticator(QObject *parent = nullptr);
|
explicit Authenticator(QObject *parent = nullptr);
|
||||||
virtual ~Authenticator() = 0;
|
virtual ~Authenticator() = 0;
|
||||||
|
|
||||||
|
virtual QString name() const = 0;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual AuthenticationReply *authenticate(const QUuid &clientId, const QString &token) = 0;
|
virtual AuthenticationReply *authenticate(ProxyClient *proxyClient) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setReplyError(AuthenticationReply *reply, AuthenticationError error);
|
||||||
|
void setReplyFinished(AuthenticationReply *reply);
|
||||||
|
|
||||||
|
AuthenticationReply *createAuthenticationReply(ProxyClient *proxyClient, QObject *parent = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // AUTHENTICATOR_H
|
#endif // AUTHENTICATOR_H
|
||||||
|
|||||||
@ -1,17 +1,25 @@
|
|||||||
|
#include "proxyclient.h"
|
||||||
#include "awsauthenticator.h"
|
#include "awsauthenticator.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
AwsAuthenticator::AwsAuthenticator(QObject *parent) :
|
AwsAuthenticator::AwsAuthenticator(QObject *parent) :
|
||||||
Authenticator(parent)
|
Authenticator(parent)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticationReply *AwsAuthenticator::authenticate(const QUuid &clientId, const QString &token)
|
QString AwsAuthenticator::name() const
|
||||||
{
|
{
|
||||||
qCDebug(dcAuthenticator()) << "Start authenticating" << clientId << "using token" << token;
|
return "AWS authenticator";
|
||||||
|
}
|
||||||
AuthenticationReply *reply = new AuthenticationReply(clientId, token, this);
|
|
||||||
|
|
||||||
|
AuthenticationReply *AwsAuthenticator::authenticate(ProxyClient *proxyClient)
|
||||||
|
{
|
||||||
|
qCDebug(dcAuthenticator()) << name() << "Start authenticating" << proxyClient << "using token" << proxyClient->token();
|
||||||
|
AuthenticationReply *reply = createAuthenticationReply(proxyClient, this);
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
#include "authenticator.h"
|
#include "authenticator.h"
|
||||||
#include "authenticationreply.h"
|
#include "authenticationreply.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class AwsAuthenticator : public Authenticator
|
class AwsAuthenticator : public Authenticator
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -13,9 +15,13 @@ public:
|
|||||||
explicit AwsAuthenticator(QObject *parent = nullptr);
|
explicit AwsAuthenticator(QObject *parent = nullptr);
|
||||||
~AwsAuthenticator() override = default;
|
~AwsAuthenticator() override = default;
|
||||||
|
|
||||||
|
QString name() const override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
AuthenticationReply *authenticate(const QUuid &clientId, const QString &token) override;
|
AuthenticationReply *authenticate(ProxyClient *proxyClient) override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // AWSAUTHENTICATOR_H
|
#endif // AWSAUTHENTICATOR_H
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
Engine *Engine::s_instance = nullptr;
|
Engine *Engine::s_instance = nullptr;
|
||||||
|
|
||||||
Engine *Engine::instance()
|
Engine *Engine::instance()
|
||||||
@ -73,12 +75,12 @@ void Engine::stop()
|
|||||||
|
|
||||||
if (m_proxyServer) {
|
if (m_proxyServer) {
|
||||||
m_proxyServer->stopServer();
|
m_proxyServer->stopServer();
|
||||||
m_proxyServer->deleteLater();
|
delete m_proxyServer;
|
||||||
m_proxyServer = nullptr;
|
m_proxyServer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_webSocketServer) {
|
if (m_webSocketServer) {
|
||||||
m_webSocketServer->deleteLater();
|
delete m_webSocketServer;
|
||||||
m_webSocketServer = nullptr;
|
m_webSocketServer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +116,7 @@ void Engine::setWebSocketServerPort(const quint16 &port)
|
|||||||
|
|
||||||
void Engine::setSslConfiguration(const QSslConfiguration &configuration)
|
void Engine::setSslConfiguration(const QSslConfiguration &configuration)
|
||||||
{
|
{
|
||||||
qCDebug(dcEngine()) << "SSL Configuration:";
|
qCDebug(dcEngine()) << "SSL certificate information:";
|
||||||
qCDebug(dcEngine()) << " Common name:" << configuration.localCertificate().issuerInfo(QSslCertificate::CommonName);
|
qCDebug(dcEngine()) << " Common name:" << configuration.localCertificate().issuerInfo(QSslCertificate::CommonName);
|
||||||
qCDebug(dcEngine()) << " Organisation:" << configuration.localCertificate().issuerInfo(QSslCertificate::Organization);
|
qCDebug(dcEngine()) << " Organisation:" << configuration.localCertificate().issuerInfo(QSslCertificate::Organization);
|
||||||
qCDebug(dcEngine()) << " Organisation unit name:" << configuration.localCertificate().issuerInfo(QSslCertificate::OrganizationalUnitName);
|
qCDebug(dcEngine()) << " Organisation unit name:" << configuration.localCertificate().issuerInfo(QSslCertificate::OrganizationalUnitName);
|
||||||
@ -134,18 +136,18 @@ void Engine::setAuthenticationServerUrl(const QUrl &url)
|
|||||||
|
|
||||||
void Engine::setAuthenticator(Authenticator *authenticator)
|
void Engine::setAuthenticator(Authenticator *authenticator)
|
||||||
{
|
{
|
||||||
|
if (m_authenticator == authenticator)
|
||||||
|
return;
|
||||||
|
|
||||||
if (m_authenticator) {
|
if (m_authenticator) {
|
||||||
qCDebug(dcEngine()) << "There is already an authenticator registered. Unregister default authenticator";
|
qCDebug(dcEngine()) << "There is already an authenticator registered. Unregister default authenticator";
|
||||||
m_authenticator->deleteLater();
|
|
||||||
m_authenticator = nullptr;
|
m_authenticator = nullptr;
|
||||||
|
|
||||||
// FIXME: do unconnect
|
// FIXME: do unconnect
|
||||||
}
|
}
|
||||||
|
|
||||||
m_authenticator = authenticator;
|
m_authenticator = authenticator;
|
||||||
|
|
||||||
|
// FIXME: connect
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Authenticator *Engine::authenticator() const
|
Authenticator *Engine::authenticator() const
|
||||||
@ -183,3 +185,5 @@ void Engine::setRunning(bool running)
|
|||||||
m_running = running;
|
m_running = running;
|
||||||
emit runningChanged(m_running);
|
emit runningChanged(m_running);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -10,6 +10,8 @@
|
|||||||
#include "websocketserver.h"
|
#include "websocketserver.h"
|
||||||
#include "authentication/authenticator.h"
|
#include "authentication/authenticator.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class Engine : public QObject
|
class Engine : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -62,4 +64,6 @@ signals:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // ENGINE_H
|
#endif // ENGINE_H
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
AuthenticationHandler::AuthenticationHandler(QObject *parent) :
|
AuthenticationHandler::AuthenticationHandler(QObject *parent) :
|
||||||
JsonHandler(parent)
|
JsonHandler(parent)
|
||||||
{
|
{
|
||||||
@ -11,31 +13,34 @@ AuthenticationHandler::AuthenticationHandler(QObject *parent) :
|
|||||||
QVariantMap params; QVariantMap returns;
|
QVariantMap params; QVariantMap returns;
|
||||||
|
|
||||||
setDescription("Authenticate", "Authenticate this connection. This should always be the first request to the server. The given id is the unique server/client uuid (i.e. the uuid of server/client).");
|
setDescription("Authenticate", "Authenticate this connection. This should always be the first request to the server. The given id is the unique server/client uuid (i.e. the uuid of server/client).");
|
||||||
params.insert("id", JsonTypes::basicTypeToString(JsonTypes::Uuid));
|
params.insert("uuid", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||||
params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String));
|
params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||||
params.insert("token", JsonTypes::basicTypeToString(JsonTypes::String));
|
params.insert("token", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||||
setParams("Authenticate", params);
|
setParams("Authenticate", params);
|
||||||
returns.insert("authenticationError", JsonTypes::authenticationErrorRef());
|
returns.insert("authenticationError", JsonTypes::authenticationErrorRef());
|
||||||
setReturns("Authenticate", returns);
|
setReturns("Authenticate", returns);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AuthenticationHandler::name() const
|
QString AuthenticationHandler::name() const
|
||||||
{
|
{
|
||||||
return "Authentication";
|
return "Authentication";
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonReply *AuthenticationHandler::Authenticate(const QVariantMap ¶ms, const QUuid &clientId)
|
JsonReply *AuthenticationHandler::Authenticate(const QVariantMap ¶ms, ProxyClient *proxyClient)
|
||||||
{
|
{
|
||||||
qCDebug(dcJsonRpc()) << "Authenticate:" << clientId.toString();
|
QString uuid = params.value("uuid").toString();
|
||||||
|
QString name = params.value("name").toString();
|
||||||
QString clientName = params.value("name").toString();
|
QString token = params.value("token").toString();
|
||||||
QString clientToken = params.value("token").toString();
|
|
||||||
QUuid clientDeviceId = QUuid(params.value("id").toString());
|
|
||||||
|
|
||||||
Q_UNUSED(clientDeviceId);
|
|
||||||
|
|
||||||
|
qCDebug(dcJsonRpc()) << "Authenticate:" << name << uuid << token;
|
||||||
JsonReply *jsonReply = createAsyncReply("Authenticate");
|
JsonReply *jsonReply = createAsyncReply("Authenticate");
|
||||||
AuthenticationReply *authReply = Engine::instance()->authenticator()->authenticate(clientId, clientToken);
|
|
||||||
|
// Set the token for this proxy client
|
||||||
|
proxyClient->setUuid(uuid);
|
||||||
|
proxyClient->setName(name);
|
||||||
|
proxyClient->setToken(token);
|
||||||
|
|
||||||
|
AuthenticationReply *authReply = Engine::instance()->authenticator()->authenticate(proxyClient);
|
||||||
connect(authReply, &AuthenticationReply::finished, this, &AuthenticationHandler::onAuthenticationFinished);
|
connect(authReply, &AuthenticationReply::finished, this, &AuthenticationHandler::onAuthenticationFinished);
|
||||||
|
|
||||||
m_runningAuthentications.insert(authReply, jsonReply);
|
m_runningAuthentications.insert(authReply, jsonReply);
|
||||||
@ -45,8 +50,22 @@ JsonReply *AuthenticationHandler::Authenticate(const QVariantMap ¶ms, const
|
|||||||
|
|
||||||
void AuthenticationHandler::onAuthenticationFinished()
|
void AuthenticationHandler::onAuthenticationFinished()
|
||||||
{
|
{
|
||||||
//AuthenticationReply *authReply = static_cast<AuthenticationReply *>(sender());
|
AuthenticationReply *authenticationReply = static_cast<AuthenticationReply *>(sender());
|
||||||
//JsonReply *jsonReply = m_runningAuthentications.take(authReply);
|
JsonReply *jsonReply = m_runningAuthentications.take(authenticationReply);
|
||||||
|
|
||||||
|
authenticationReply->deleteLater();
|
||||||
|
|
||||||
|
qCDebug(dcJsonRpc()) << "Authentication respons ready for" << authenticationReply->proxyClient() << authenticationReply->error();
|
||||||
|
|
||||||
|
if (authenticationReply->error() != Authenticator::AuthenticationErrorNoError) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Authentication error occured" << authenticationReply->error();
|
||||||
|
jsonReply->setSuccess(true);
|
||||||
|
} else {
|
||||||
|
jsonReply->setSuccess(false);
|
||||||
|
}
|
||||||
|
|
||||||
//emit asyncReply()
|
jsonReply->setData(errorToReply(authenticationReply->error()));
|
||||||
|
jsonReply->finished();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,8 @@
|
|||||||
#include "jsonhandler.h"
|
#include "jsonhandler.h"
|
||||||
#include "authentication/authenticationreply.h"
|
#include "authentication/authenticationreply.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class AuthenticationHandler : public JsonHandler
|
class AuthenticationHandler : public JsonHandler
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -15,7 +17,7 @@ public:
|
|||||||
|
|
||||||
QString name() const override;
|
QString name() const override;
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *Authenticate(const QVariantMap ¶ms, const QUuid &clientId);
|
Q_INVOKABLE JsonReply *Authenticate(const QVariantMap ¶ms, ProxyClient *proxyClient);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHash<AuthenticationReply *, JsonReply *> m_runningAuthentications;
|
QHash<AuthenticationReply *, JsonReply *> m_runningAuthentications;
|
||||||
@ -26,4 +28,6 @@ private slots:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // AUTHENTICATIONHANDLER_H
|
#endif // AUTHENTICATIONHANDLER_H
|
||||||
|
|||||||
@ -7,6 +7,8 @@
|
|||||||
#include "jsontypes.h"
|
#include "jsontypes.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
JsonHandler::JsonHandler(QObject *parent):
|
JsonHandler::JsonHandler(QObject *parent):
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
{
|
{
|
||||||
@ -106,6 +108,13 @@ void JsonHandler::setReturns(const QString &methodName, const QVariantMap &retur
|
|||||||
qCWarning(dcJsonRpc()) << "Cannot set returns. No such method:" << methodName;
|
qCWarning(dcJsonRpc()) << "Cannot set returns. No such method:" << methodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantMap JsonHandler::errorToReply(Authenticator::AuthenticationError error) const
|
||||||
|
{
|
||||||
|
QVariantMap returns;
|
||||||
|
returns.insert("authenticationError", JsonTypes::authenticationErrorToString(error));
|
||||||
|
return returns;
|
||||||
|
}
|
||||||
|
|
||||||
JsonReply *JsonHandler::createReply(const QVariantMap &data) const
|
JsonReply *JsonHandler::createReply(const QVariantMap &data) const
|
||||||
{
|
{
|
||||||
return JsonReply::createReply(const_cast<JsonHandler*>(this), data);
|
return JsonReply::createReply(const_cast<JsonHandler*>(this), data);
|
||||||
@ -115,3 +124,5 @@ JsonReply *JsonHandler::createAsyncReply(const QString &method) const
|
|||||||
{
|
{
|
||||||
return JsonReply::createAsyncReply(const_cast<JsonHandler*>(this), method);
|
return JsonReply::createAsyncReply(const_cast<JsonHandler*>(this), method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -7,6 +7,10 @@
|
|||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
#include <QMetaMethod>
|
#include <QMetaMethod>
|
||||||
|
|
||||||
|
#include "authentication/authenticator.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class JsonReply;
|
class JsonReply;
|
||||||
|
|
||||||
class JsonHandler : public QObject
|
class JsonHandler : public QObject
|
||||||
@ -36,8 +40,14 @@ protected:
|
|||||||
void setParams(const QString &methodName, const QVariantMap ¶ms);
|
void setParams(const QString &methodName, const QVariantMap ¶ms);
|
||||||
void setReturns(const QString &methodName, const QVariantMap &returns);
|
void setReturns(const QString &methodName, const QVariantMap &returns);
|
||||||
|
|
||||||
|
QVariantMap errorToReply(Authenticator::AuthenticationError error) const;
|
||||||
|
|
||||||
|
|
||||||
JsonReply *createReply(const QVariantMap &data) const;
|
JsonReply *createReply(const QVariantMap &data) const;
|
||||||
JsonReply *createAsyncReply(const QString &method) const;
|
JsonReply *createAsyncReply(const QString &method) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // JSONHANDLER_H
|
#endif // JSONHANDLER_H
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#include "jsonreply.h"
|
#include "jsonreply.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
JsonReply *JsonReply::createReply(JsonHandler *handler, const QVariantMap &data)
|
JsonReply *JsonReply::createReply(JsonHandler *handler, const QVariantMap &data)
|
||||||
{
|
{
|
||||||
return new JsonReply(TypeSync, handler, QString(), data);
|
return new JsonReply(TypeSync, handler, QString(), data);
|
||||||
@ -35,14 +37,14 @@ QString JsonReply::method() const
|
|||||||
return m_method;
|
return m_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
QUuid JsonReply::connectionId() const
|
QUuid JsonReply::clientId() const
|
||||||
{
|
{
|
||||||
return m_connectionId;
|
return m_clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonReply::setClientId(const QUuid &connectionId)
|
void JsonReply::setClientId(const QUuid &clientId)
|
||||||
{
|
{
|
||||||
m_connectionId = connectionId;
|
m_clientId = clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
int JsonReply::commandId() const
|
int JsonReply::commandId() const
|
||||||
@ -55,6 +57,16 @@ void JsonReply::setCommandId(int commandId)
|
|||||||
m_commandId = commandId;
|
m_commandId = commandId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JsonReply::success() const
|
||||||
|
{
|
||||||
|
return m_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonReply::setSuccess(bool success)
|
||||||
|
{
|
||||||
|
m_success = success;
|
||||||
|
}
|
||||||
|
|
||||||
bool JsonReply::timedOut() const
|
bool JsonReply::timedOut() const
|
||||||
{
|
{
|
||||||
return m_timedOut;
|
return m_timedOut;
|
||||||
@ -71,12 +83,16 @@ void JsonReply::timeout()
|
|||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonReply::JsonReply(JsonReply::Type type, JsonHandler *handler, const QString &method, const QVariantMap &data):
|
JsonReply::JsonReply(JsonReply::Type type, JsonHandler *handler, const QString &method, const QVariantMap &data, bool success):
|
||||||
m_type(type),
|
m_type(type),
|
||||||
m_data(data),
|
m_data(data),
|
||||||
m_handler(handler),
|
m_handler(handler),
|
||||||
m_method(method),
|
m_method(method),
|
||||||
m_timedOut(false)
|
m_success(success)
|
||||||
{
|
{
|
||||||
connect(&m_timeout, &QTimer::timeout, this, &JsonReply::timeout);
|
connect(&m_timeout, &QTimer::timeout, this, &JsonReply::timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#include "jsonhandler.h"
|
#include "jsonhandler.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class JsonReply: public QObject
|
class JsonReply: public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -16,22 +18,29 @@ public:
|
|||||||
TypeAsync
|
TypeAsync
|
||||||
};
|
};
|
||||||
|
|
||||||
|
friend class JsonRpcServer;
|
||||||
|
|
||||||
static JsonReply *createReply(JsonHandler *handler, const QVariantMap &data);
|
static JsonReply *createReply(JsonHandler *handler, const QVariantMap &data);
|
||||||
static JsonReply *createAsyncReply(JsonHandler *handler, const QString &method);
|
static JsonReply *createAsyncReply(JsonHandler *handler, const QString &method);
|
||||||
|
|
||||||
Type type() const;
|
Type type() const;
|
||||||
|
|
||||||
QVariantMap data() const;
|
QVariantMap data() const;
|
||||||
void setData(const QVariantMap &data);
|
void setData(const QVariantMap &data);
|
||||||
|
|
||||||
JsonHandler *handler() const;
|
JsonHandler *handler() const;
|
||||||
|
|
||||||
QString method() const;
|
QString method() const;
|
||||||
|
|
||||||
QUuid connectionId() const;
|
QUuid clientId() const;
|
||||||
void setClientId(const QUuid &connectionId);
|
void setClientId(const QUuid &clientId);
|
||||||
|
|
||||||
int commandId() const;
|
int commandId() const;
|
||||||
void setCommandId(int commandId);
|
void setCommandId(int commandId);
|
||||||
|
|
||||||
|
bool success() const;
|
||||||
|
void setSuccess(bool success);
|
||||||
|
|
||||||
bool timedOut() const;
|
bool timedOut() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
@ -44,17 +53,22 @@ private slots:
|
|||||||
void timeout();
|
void timeout();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JsonReply(Type type, JsonHandler *handler, const QString &method, const QVariantMap &data = QVariantMap());
|
JsonReply(Type type, JsonHandler *handler, const QString &method, const QVariantMap &data = QVariantMap(), bool success = true);
|
||||||
Type m_type;
|
|
||||||
|
Type m_type = TypeSync;
|
||||||
QVariantMap m_data;
|
QVariantMap m_data;
|
||||||
|
|
||||||
JsonHandler *m_handler;
|
JsonHandler *m_handler = nullptr;
|
||||||
|
|
||||||
QString m_method;
|
QString m_method;
|
||||||
QUuid m_connectionId;
|
QUuid m_clientId;
|
||||||
int m_commandId;
|
int m_commandId;
|
||||||
bool m_timedOut;
|
bool m_timedOut = false;
|
||||||
|
bool m_success = false;
|
||||||
|
|
||||||
QTimer m_timeout;
|
QTimer m_timeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // JSONRPCREPLY_H
|
#endif // JSONRPCREPLY_H
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
bool JsonTypes::s_initialized = false;
|
bool JsonTypes::s_initialized = false;
|
||||||
QString JsonTypes::s_lastError;
|
QString JsonTypes::s_lastError;
|
||||||
|
|
||||||
@ -198,6 +200,9 @@ QPair<bool, QString> JsonTypes::validateList(const QVariantList &templateList, c
|
|||||||
QPair<bool, QString> JsonTypes::validateBasicType(const QVariant &variant)
|
QPair<bool, QString> JsonTypes::validateBasicType(const QVariant &variant)
|
||||||
{
|
{
|
||||||
if (variant.canConvert(QVariant::Uuid) && QVariant(variant).convert(QVariant::Uuid)) {
|
if (variant.canConvert(QVariant::Uuid) && QVariant(variant).convert(QVariant::Uuid)) {
|
||||||
|
if (QUuid(variant.toString()).isNull()) {
|
||||||
|
return report(false, "Invalid uuid format.");
|
||||||
|
}
|
||||||
return report(true, "");
|
return report(true, "");
|
||||||
}
|
}
|
||||||
if (variant.canConvert(QVariant::String) && QVariant(variant).convert(QVariant::String)) {
|
if (variant.canConvert(QVariant::String) && QVariant(variant).convert(QVariant::String)) {
|
||||||
@ -259,3 +264,5 @@ QVariantList JsonTypes::enumToStrings(const QMetaObject &metaObject, const QStri
|
|||||||
}
|
}
|
||||||
return enumStrings;
|
return enumStrings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include "authentication/authenticator.h"
|
#include "authentication/authenticator.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
#define DECLARE_OBJECT(typeName, jsonName) \
|
#define DECLARE_OBJECT(typeName, jsonName) \
|
||||||
public: \
|
public: \
|
||||||
static QString typeName##Ref() { return QStringLiteral("$ref:") + QStringLiteral(jsonName); if (!s_initialized) { init(); } } \
|
static QString typeName##Ref() { return QStringLiteral("$ref:") + QStringLiteral(jsonName); if (!s_initialized) { init(); } } \
|
||||||
@ -87,4 +89,6 @@ private:
|
|||||||
static QVariantList enumToStrings(const QMetaObject &metaObject, const QString &enumName);
|
static QVariantList enumToStrings(const QMetaObject &metaObject, const QString &enumName);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // JSONTYPES_H
|
#endif // JSONTYPES_H
|
||||||
|
|||||||
@ -4,8 +4,11 @@
|
|||||||
#include "jsonrpc/jsontypes.h"
|
#include "jsonrpc/jsontypes.h"
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonParseError>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
JsonRpcServer::JsonRpcServer(QObject *parent) :
|
JsonRpcServer::JsonRpcServer(QObject *parent) :
|
||||||
JsonHandler(parent)
|
JsonHandler(parent)
|
||||||
{
|
{
|
||||||
@ -13,7 +16,8 @@ JsonRpcServer::JsonRpcServer(QObject *parent) :
|
|||||||
QVariantMap params; QVariantMap returns;
|
QVariantMap params; QVariantMap returns;
|
||||||
|
|
||||||
params.clear(); returns.clear();
|
params.clear(); returns.clear();
|
||||||
setDescription("Hello", "Once connected to this server, a client can get information about the server by saying Hello. The response informs the client about the server.");
|
setDescription("Hello", "Once connected to this server, a client can get information about the server by saying Hello. "
|
||||||
|
"The response informs the client about the server.");
|
||||||
setParams("Hello", params);
|
setParams("Hello", params);
|
||||||
returns.insert("server", JsonTypes::basicTypeToString(JsonTypes::String));
|
returns.insert("server", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||||
returns.insert("name", JsonTypes::basicTypeToString(JsonTypes::String));
|
returns.insert("name", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||||
@ -35,6 +39,9 @@ JsonRpcServer::JsonRpcServer(QObject *parent) :
|
|||||||
JsonRpcServer::~JsonRpcServer()
|
JsonRpcServer::~JsonRpcServer()
|
||||||
{
|
{
|
||||||
qCDebug(dcJsonRpc()) << "Shutting down Json RPC server";
|
qCDebug(dcJsonRpc()) << "Shutting down Json RPC server";
|
||||||
|
foreach (JsonHandler *handler, m_handlers.values()) {
|
||||||
|
unregisterHandler(handler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString JsonRpcServer::name() const
|
QString JsonRpcServer::name() const
|
||||||
@ -42,33 +49,10 @@ QString JsonRpcServer::name() const
|
|||||||
return "RemoteProxy";
|
return "RemoteProxy";
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<QString, JsonHandler *> JsonRpcServer::handlers() const
|
JsonReply *JsonRpcServer::Hello(const QVariantMap ¶ms, ProxyClient *proxyClient) const
|
||||||
{
|
|
||||||
return m_handlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JsonRpcServer::registerTransportInterface(TransportInterface *interface)
|
|
||||||
{
|
|
||||||
if (m_interfaces.contains(interface))
|
|
||||||
return;
|
|
||||||
|
|
||||||
connect(interface, &TransportInterface::clientConnected, this, &JsonRpcServer::clientConnected);
|
|
||||||
connect(interface, &TransportInterface::clientDisconnected, this, &JsonRpcServer::clientDisconnected);
|
|
||||||
connect(interface, &TransportInterface::dataAvailable, this, &JsonRpcServer::processData);
|
|
||||||
m_interfaces.append(interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JsonRpcServer::unregisterTransportInterface(TransportInterface *interface)
|
|
||||||
{
|
|
||||||
disconnect(interface, &TransportInterface::clientConnected, this, &JsonRpcServer::clientConnected);
|
|
||||||
disconnect(interface, &TransportInterface::clientDisconnected, this, &JsonRpcServer::clientDisconnected);
|
|
||||||
disconnect(interface, &TransportInterface::dataAvailable, this, &JsonRpcServer::processData);
|
|
||||||
m_interfaces.removeOne(interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonReply *JsonRpcServer::Hello(const QVariantMap ¶ms) const
|
|
||||||
{
|
{
|
||||||
Q_UNUSED(params)
|
Q_UNUSED(params)
|
||||||
|
Q_UNUSED(proxyClient)
|
||||||
|
|
||||||
QVariantMap data;
|
QVariantMap data;
|
||||||
data.insert("server", QCoreApplication::applicationName());
|
data.insert("server", QCoreApplication::applicationName());
|
||||||
@ -79,9 +63,12 @@ JsonReply *JsonRpcServer::Hello(const QVariantMap ¶ms) const
|
|||||||
return createReply(data);
|
return createReply(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonReply *JsonRpcServer::Introspect(const QVariantMap ¶ms) const
|
JsonReply *JsonRpcServer::Introspect(const QVariantMap ¶ms, ProxyClient *proxyClient) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(params)
|
Q_UNUSED(params)
|
||||||
|
Q_UNUSED(proxyClient)
|
||||||
|
|
||||||
|
qCDebug(dcJsonRpc()) << "Introspect called.";
|
||||||
|
|
||||||
QVariantMap data;
|
QVariantMap data;
|
||||||
data.insert("types", JsonTypes::allTypes());
|
data.insert("types", JsonTypes::allTypes());
|
||||||
@ -103,7 +90,7 @@ JsonReply *JsonRpcServer::Introspect(const QVariantMap ¶ms) const
|
|||||||
return createReply(data);
|
return createReply(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonRpcServer::sendResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QVariantMap ¶ms)
|
void JsonRpcServer::sendResponse(ProxyClient *client, int commandId, const QVariantMap ¶ms)
|
||||||
{
|
{
|
||||||
QVariantMap response;
|
QVariantMap response;
|
||||||
response.insert("id", commandId);
|
response.insert("id", commandId);
|
||||||
@ -112,10 +99,10 @@ void JsonRpcServer::sendResponse(TransportInterface *interface, const QUuid &cli
|
|||||||
|
|
||||||
QByteArray data = QJsonDocument::fromVariant(response).toJson(QJsonDocument::Compact);
|
QByteArray data = QJsonDocument::fromVariant(response).toJson(QJsonDocument::Compact);
|
||||||
qCDebug(dcJsonRpcTraffic()) << "Sending data:" << data;
|
qCDebug(dcJsonRpcTraffic()) << "Sending data:" << data;
|
||||||
interface->sendData(clientId, data);
|
client->interface()->sendData(client->clientId(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonRpcServer::sendErrorResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QString &error)
|
void JsonRpcServer::sendErrorResponse(ProxyClient *client, int commandId, const QString &error)
|
||||||
{
|
{
|
||||||
QVariantMap errorResponse;
|
QVariantMap errorResponse;
|
||||||
errorResponse.insert("id", commandId);
|
errorResponse.insert("id", commandId);
|
||||||
@ -124,7 +111,7 @@ void JsonRpcServer::sendErrorResponse(TransportInterface *interface, const QUuid
|
|||||||
|
|
||||||
QByteArray data = QJsonDocument::fromVariant(errorResponse).toJson(QJsonDocument::Compact);
|
QByteArray data = QJsonDocument::fromVariant(errorResponse).toJson(QJsonDocument::Compact);
|
||||||
qCDebug(dcJsonRpcTraffic()) << "Sending data:" << data;
|
qCDebug(dcJsonRpcTraffic()) << "Sending data:" << data;
|
||||||
interface->sendData(clientId, data);
|
client->interface()->sendData(client->clientId(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString JsonRpcServer::formatAssertion(const QString &targetNamespace, const QString &method, JsonHandler *handler, const QVariantMap &data) const
|
QString JsonRpcServer::formatAssertion(const QString &targetNamespace, const QString &method, JsonHandler *handler, const QVariantMap &data) const
|
||||||
@ -149,25 +136,149 @@ void JsonRpcServer::registerHandler(JsonHandler *handler)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonRpcServer::unregisterHandler(JsonHandler *handler)
|
||||||
|
{
|
||||||
|
m_handlers.remove(handler->name());
|
||||||
|
qCDebug(dcJsonRpc()) << "Unregister handler" << handler->name();
|
||||||
|
|
||||||
|
for (int i = 0; i < handler->metaObject()->methodCount(); ++i) {
|
||||||
|
QMetaMethod method = handler->metaObject()->method(i);
|
||||||
|
if (method.methodType() == QMetaMethod::Signal && QString(method.name()).contains(QRegExp("^[A-Z]"))) {
|
||||||
|
QObject::connect(handler, method, this, metaObject()->method(metaObject()->indexOfSlot("sendNotification(QVariantMap)")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void JsonRpcServer::setup()
|
void JsonRpcServer::setup()
|
||||||
{
|
{
|
||||||
registerHandler(this);
|
registerHandler(this);
|
||||||
registerHandler(new AuthenticationHandler(this));
|
registerHandler(new AuthenticationHandler(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonRpcServer::clientConnected(const QUuid &clientId)
|
void JsonRpcServer::asyncReplyFinished()
|
||||||
{
|
{
|
||||||
Q_UNUSED(clientId)
|
JsonReply *reply = static_cast<JsonReply *>(sender());
|
||||||
|
ProxyClient *proxyClient = m_asyncReplies.take(reply);
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
if (!proxyClient) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Got an async reply but the client does not exist any more";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(proxyClient, reply->commandId(), reply->data());
|
||||||
|
if (!reply->success()) {
|
||||||
|
// Disconnect this client since the request was not successfully
|
||||||
|
proxyClient->interface()->killClientConnection(proxyClient->clientId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendErrorResponse(proxyClient, reply->commandId(), "Command timed out");
|
||||||
|
// Disconnect this client since he requested something that created a timeout
|
||||||
|
proxyClient->interface()->killClientConnection(proxyClient->clientId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonRpcServer::clientDisconnected(const QUuid &clientId)
|
void JsonRpcServer::registerClient(ProxyClient *proxyClient)
|
||||||
{
|
{
|
||||||
Q_UNUSED(clientId)
|
qCDebug(dcJsonRpc()) << "Register client" << proxyClient;
|
||||||
|
if (m_clients.contains(proxyClient)) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Client already registered" << proxyClient;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_clients.append(proxyClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonRpcServer::processData(const QUuid &clientId, const QByteArray &data)
|
void JsonRpcServer::unregisterClient(ProxyClient *proxyClient)
|
||||||
{
|
{
|
||||||
Q_UNUSED(clientId)
|
qCDebug(dcJsonRpc()) << "Unregister client" << proxyClient;
|
||||||
Q_UNUSED(data)
|
if (!m_clients.contains(proxyClient)) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Client was not registered" << proxyClient;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_clients.removeAll(proxyClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonRpcServer::processData(ProxyClient *proxyClient, const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (!m_clients.contains(proxyClient))
|
||||||
|
return;
|
||||||
|
|
||||||
|
qCDebug(dcJsonRpcTraffic()) << "Incoming data from" << proxyClient << ": " << 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(proxyClient, -1, QString("Failed to parse JSON data: %1").arg(error.errorString()));
|
||||||
|
proxyClient->interface()->killClientConnection(proxyClient->clientId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap message = jsonDoc.toVariant().toMap();
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
int commandId = message.value("id").toInt(&success);
|
||||||
|
if (!success) {
|
||||||
|
qCWarning(dcJsonRpc()) << "Error parsing command. Missing \"id\":" << message;
|
||||||
|
sendErrorResponse(proxyClient, -1, "Error parsing command. Missing 'id'");
|
||||||
|
proxyClient->interface()->killClientConnection(proxyClient->clientId());
|
||||||
|
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(proxyClient, commandId, QString("Error parsing method. Got: '%1'', Expected: 'Namespace.method'").arg(message.value("method").toString()));
|
||||||
|
proxyClient->interface()->killClientConnection(proxyClient->clientId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString targetNamespace = commandList.first();
|
||||||
|
QString method = commandList.last();
|
||||||
|
|
||||||
|
JsonHandler *handler = m_handlers.value(targetNamespace);
|
||||||
|
if (!handler) {
|
||||||
|
sendErrorResponse(proxyClient, commandId, "No such namespace");
|
||||||
|
proxyClient->interface()->killClientConnection(proxyClient->clientId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler->hasMethod(method)) {
|
||||||
|
sendErrorResponse(proxyClient, commandId, "No such namespace");
|
||||||
|
proxyClient->interface()->killClientConnection(proxyClient->clientId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap params = message.value("params").toMap();
|
||||||
|
|
||||||
|
QPair<bool, QString> validationResult = handler->validateParams(method, params);
|
||||||
|
if (!validationResult.first) {
|
||||||
|
sendErrorResponse(proxyClient, commandId, "Invalid params: " + validationResult.second);
|
||||||
|
proxyClient->interface()->killClientConnection(proxyClient->clientId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonReply *reply;
|
||||||
|
QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(JsonReply*, reply), Q_ARG(QVariantMap, params), Q_ARG(ProxyClient *, proxyClient));
|
||||||
|
if (reply->type() == JsonReply::TypeAsync) {
|
||||||
|
m_asyncReplies.insert(reply, proxyClient);
|
||||||
|
reply->setClientId(proxyClient->clientId());
|
||||||
|
reply->setCommandId(commandId);
|
||||||
|
connect(reply, &JsonReply::finished, this, &JsonRpcServer::asyncReplyFinished);
|
||||||
|
reply->startWait();
|
||||||
|
} else {
|
||||||
|
Q_ASSERT_X((targetNamespace == "RemoteProxy" && method == "Introspect") || handler->validateReturns(method, reply->data()).first
|
||||||
|
,"validating return value", formatAssertion(targetNamespace, method, handler, reply->data()).toLatin1().data());
|
||||||
|
reply->setClientId(proxyClient->clientId());
|
||||||
|
reply->setCommandId(commandId);
|
||||||
|
sendResponse(proxyClient, commandId, reply->data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -2,11 +2,16 @@
|
|||||||
#define JSONRPCSERVER_H
|
#define JSONRPCSERVER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "proxyclient.h"
|
||||||
|
#include "jsonrpc/jsonreply.h"
|
||||||
#include "transportinterface.h"
|
#include "transportinterface.h"
|
||||||
#include "jsonrpc/jsonhandler.h"
|
#include "jsonrpc/jsonhandler.h"
|
||||||
#include "jsonrpc/authenticationhandler.h"
|
#include "jsonrpc/authenticationhandler.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class JsonRpcServer : public JsonHandler
|
class JsonRpcServer : public JsonHandler
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -16,39 +21,40 @@ public:
|
|||||||
|
|
||||||
QString name() const override;
|
QString name() const override;
|
||||||
|
|
||||||
QHash<QString, JsonHandler *> handlers() const;
|
Q_INVOKABLE JsonReply *Hello(const QVariantMap ¶ms, ProxyClient *proxyClient = nullptr) const;
|
||||||
|
Q_INVOKABLE JsonReply *Introspect(const QVariantMap ¶ms, ProxyClient *proxyClient = nullptr) const;
|
||||||
|
|
||||||
void registerTransportInterface(TransportInterface *interface);
|
signals:
|
||||||
void unregisterTransportInterface(TransportInterface *interface);
|
void TunnelEstablished(const QVariantMap ¶ms, ProxyClient *proxyClient = nullptr);
|
||||||
|
|
||||||
Q_INVOKABLE JsonReply *Hello(const QVariantMap ¶ms) const;
|
|
||||||
Q_INVOKABLE JsonReply *Introspect(const QVariantMap ¶ms) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<TransportInterface *> m_interfaces;
|
|
||||||
QHash<QString, JsonHandler *> m_handlers;
|
QHash<QString, JsonHandler *> m_handlers;
|
||||||
QHash<JsonReply *, TransportInterface *> m_asyncReplies;
|
QHash<JsonReply *, ProxyClient *> m_asyncReplies;
|
||||||
QHash<QUuid, TransportInterface*> m_clientTransports;
|
QList<ProxyClient *> m_clients;
|
||||||
QHash<QString, JsonReply*> m_pairingRequests;
|
|
||||||
|
|
||||||
int m_notificationId;
|
int m_notificationId;
|
||||||
|
|
||||||
void sendResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QVariantMap ¶ms = QVariantMap());
|
void sendResponse(ProxyClient *client, int commandId, const QVariantMap ¶ms = QVariantMap());
|
||||||
void sendErrorResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QString &error);
|
void sendErrorResponse(ProxyClient *client, int commandId, const QString &error);
|
||||||
|
|
||||||
QString formatAssertion(const QString &targetNamespace, const QString &method, JsonHandler *handler, const QVariantMap &data) const;
|
QString formatAssertion(const QString &targetNamespace, const QString &method, JsonHandler *handler, const QVariantMap &data) const;
|
||||||
|
|
||||||
void registerHandler(JsonHandler *handler);
|
void registerHandler(JsonHandler *handler);
|
||||||
|
void unregisterHandler(JsonHandler *handler);
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void setup();
|
void setup();
|
||||||
void clientConnected(const QUuid &clientId);
|
void asyncReplyFinished();
|
||||||
void clientDisconnected(const QUuid &clientId);
|
|
||||||
void processData(const QUuid &clientId, const QByteArray &data);
|
public slots:
|
||||||
// void sendNotification(const QVariantMap ¶ms);
|
// Client registration for JSON RPC traffic
|
||||||
// void asyncReplyFinished();
|
void registerClient(ProxyClient *proxyClient);
|
||||||
|
void unregisterClient(ProxyClient *proxyClient);
|
||||||
|
|
||||||
|
// Process data from client
|
||||||
|
void processData(ProxyClient *proxyClient, const QByteArray &data);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // JSONRPCSERVER_H
|
#endif // JSONRPCSERVER_H
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
#include "proxyclient.h"
|
#include "proxyclient.h"
|
||||||
|
|
||||||
ProxyClient::ProxyClient(const QUuid &clientId, QObject *parent) :
|
namespace remoteproxy {
|
||||||
|
|
||||||
|
ProxyClient::ProxyClient(TransportInterface *interface, const QUuid &clientId, QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
|
m_interface(interface),
|
||||||
m_clientId(clientId)
|
m_clientId(clientId)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -17,7 +20,77 @@ bool ProxyClient::authenticated() const
|
|||||||
return m_authenticated;
|
return m_authenticated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProxyClient::setAuthenticated(bool authenticated)
|
||||||
|
{
|
||||||
|
if (m_authenticated == authenticated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_authenticated = authenticated;
|
||||||
|
emit authenticatedChanged(m_authenticated);
|
||||||
|
}
|
||||||
|
|
||||||
bool ProxyClient::tunnelConnected() const
|
bool ProxyClient::tunnelConnected() const
|
||||||
{
|
{
|
||||||
return m_tunnelConnected;
|
return m_tunnelConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProxyClient::setTunnelConnected(bool tunnelConnected)
|
||||||
|
{
|
||||||
|
if (m_tunnelConnected == tunnelConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_tunnelConnected = tunnelConnected;
|
||||||
|
emit tunnelConnectedChanged(m_tunnelConnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransportInterface *ProxyClient::interface() const
|
||||||
|
{
|
||||||
|
return m_interface;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProxyClient::uuid() const
|
||||||
|
{
|
||||||
|
return m_uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyClient::setUuid(const QString &uuid)
|
||||||
|
{
|
||||||
|
m_uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProxyClient::name() const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyClient::setName(const QString &name)
|
||||||
|
{
|
||||||
|
m_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProxyClient::token() const
|
||||||
|
{
|
||||||
|
return m_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyClient::setToken(const QString &token)
|
||||||
|
{
|
||||||
|
m_token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, ProxyClient *proxyClient)
|
||||||
|
{
|
||||||
|
debug.nospace() << "ProxyClient(" << proxyClient->interface()->serverName();
|
||||||
|
debug.nospace() << ", " << proxyClient->clientId().toString() << ") :" << endl;
|
||||||
|
debug.nospace() << " tunnel: " << proxyClient->tunnelConnected() << endl;
|
||||||
|
debug.nospace() << " authenticated: " << proxyClient->authenticated() << endl;
|
||||||
|
if (!proxyClient->name().isEmpty() && !proxyClient->token().isEmpty() && !proxyClient->uuid().isEmpty()) {
|
||||||
|
debug.nospace() << " name: " << proxyClient->name() << endl;
|
||||||
|
debug.nospace() << " uuid: " << proxyClient->uuid() << endl;
|
||||||
|
debug.nospace() << " token: " << proxyClient->token() << endl;
|
||||||
|
|
||||||
|
}
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -2,25 +2,58 @@
|
|||||||
#define PROXYCLIENT_H
|
#define PROXYCLIENT_H
|
||||||
|
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
#include <QDebug>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "transportinterface.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class ProxyClient : public QObject
|
class ProxyClient : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ProxyClient(const QUuid &clientId, QObject *parent = nullptr);
|
explicit ProxyClient(TransportInterface *interface, const QUuid &clientId, QObject *parent = nullptr);
|
||||||
|
|
||||||
QUuid clientId() const;
|
QUuid clientId() const;
|
||||||
|
|
||||||
bool authenticated() const;
|
bool authenticated() const;
|
||||||
|
void setAuthenticated(bool authenticated);
|
||||||
|
|
||||||
bool tunnelConnected() const;
|
bool tunnelConnected() const;
|
||||||
|
void setTunnelConnected(bool tunnelConnected);
|
||||||
|
|
||||||
|
TransportInterface *interface() const;
|
||||||
|
|
||||||
|
// Properties from auth request
|
||||||
|
QString uuid() const;
|
||||||
|
void setUuid(const QString &uuid);
|
||||||
|
|
||||||
|
QString name() const;
|
||||||
|
void setName(const QString &name);
|
||||||
|
|
||||||
|
QString token() const;
|
||||||
|
void setToken(const QString &token);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
TransportInterface *m_interface = nullptr;
|
||||||
QUuid m_clientId;
|
QUuid m_clientId;
|
||||||
bool m_authenticated = false;
|
bool m_authenticated = false;
|
||||||
bool m_tunnelConnected = false;
|
bool m_tunnelConnected = false;
|
||||||
|
QString m_uuid;
|
||||||
|
QString m_name;
|
||||||
|
QString m_token;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void authenticatedChanged(bool authenticated);
|
||||||
|
void tunnelConnectedChanged(bool tunnelConnected);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QDebug operator<< (QDebug debug, ProxyClient *proxyClient);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // PROXYCLIENT_H
|
#endif // PROXYCLIENT_H
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
#include "proxyconfiguration.h"
|
#include "proxyconfiguration.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
ProxyConfiguration::ProxyConfiguration(QObject *parent) : QObject(parent)
|
ProxyConfiguration::ProxyConfiguration(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class ProxyConfiguration : public QObject
|
class ProxyConfiguration : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -15,4 +17,6 @@ public slots:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // PROXYCONFIGURATION_H
|
#endif // PROXYCONFIGURATION_H
|
||||||
|
|||||||
@ -1,11 +1,21 @@
|
|||||||
#include "proxyserver.h"
|
#include "proxyserver.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
ProxyServer::ProxyServer(QObject *parent) : QObject(parent)
|
ProxyServer::ProxyServer(QObject *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
|
m_jsonRpcServer = new JsonRpcServer(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProxyServer::~ProxyServer()
|
||||||
|
{
|
||||||
|
qCDebug(dcProxyServer()) << "Shutting down proxy server";
|
||||||
|
}
|
||||||
|
|
||||||
void ProxyServer::registerTransportInterface(TransportInterface *interface)
|
void ProxyServer::registerTransportInterface(TransportInterface *interface)
|
||||||
{
|
{
|
||||||
qCDebug(dcProxyServer()) << "Register transport interface" << interface->serverName();
|
qCDebug(dcProxyServer()) << "Register transport interface" << interface->serverName();
|
||||||
@ -22,12 +32,23 @@ void ProxyServer::registerTransportInterface(TransportInterface *interface)
|
|||||||
m_transportInterfaces.append(interface);
|
m_transportInterfaces.append(interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProxyServer::sendResponse(TransportInterface *interface, const QUuid &clientId, const QVariantMap &response)
|
||||||
|
{
|
||||||
|
QByteArray data = QJsonDocument::fromVariant(response).toJson(QJsonDocument::Compact);
|
||||||
|
qCDebug(dcJsonRpcTraffic()) << "Sending data:" << data;
|
||||||
|
interface->sendData(clientId, data);
|
||||||
|
}
|
||||||
|
|
||||||
void ProxyServer::onClientConnected(const QUuid &clientId)
|
void ProxyServer::onClientConnected(const QUuid &clientId)
|
||||||
{
|
{
|
||||||
TransportInterface *interface = static_cast<TransportInterface *>(sender());
|
TransportInterface *interface = static_cast<TransportInterface *>(sender());
|
||||||
|
|
||||||
qCDebug(dcProxyServer()) << "New client connected" << interface->serverName() << clientId.toString();
|
qCDebug(dcProxyServer()) << "New client connected" << interface->serverName() << clientId.toString();
|
||||||
|
|
||||||
m_unauthenticatedClients.append(clientId);
|
ProxyClient *proxyClient = new ProxyClient(interface, clientId, this);
|
||||||
|
m_proxyClients.insert(clientId, proxyClient);
|
||||||
|
|
||||||
|
m_jsonRpcServer->registerClient(proxyClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyServer::onClientDisconnected(const QUuid &clientId)
|
void ProxyServer::onClientDisconnected(const QUuid &clientId)
|
||||||
@ -35,14 +56,33 @@ void ProxyServer::onClientDisconnected(const QUuid &clientId)
|
|||||||
TransportInterface *interface = static_cast<TransportInterface *>(sender());
|
TransportInterface *interface = static_cast<TransportInterface *>(sender());
|
||||||
qCDebug(dcProxyServer()) << "Client disconnected" << interface->serverName() << clientId.toString();
|
qCDebug(dcProxyServer()) << "Client disconnected" << interface->serverName() << clientId.toString();
|
||||||
|
|
||||||
// Clean up this client since it does not exist any more
|
if (m_proxyClients.contains(clientId)) {
|
||||||
m_unauthenticatedClients.removeAll(clientId);
|
ProxyClient *proxyClient = m_proxyClients.take(clientId);
|
||||||
|
m_jsonRpcServer->unregisterClient(proxyClient);
|
||||||
|
proxyClient->deleteLater();
|
||||||
|
// Check if client is in tunnel and clean up tunnel
|
||||||
|
// Disconnect also the other tunnel client
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Clean up this client since it does not exist any more
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyServer::onClientDataAvailable(const QUuid &clientId, const QByteArray &data)
|
void ProxyServer::onClientDataAvailable(const QUuid &clientId, const QByteArray &data)
|
||||||
{
|
{
|
||||||
TransportInterface *interface = static_cast<TransportInterface *>(sender());
|
ProxyClient *proxyClient = m_proxyClients.value(clientId);
|
||||||
qCDebug(dcProxyServer()) << "Client data available" << interface->serverName() << clientId.toString() << qUtf8Printable(data);
|
if (!proxyClient) {
|
||||||
|
qCWarning(dcProxyServer()) << "Could not find client for uuid" << clientId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(dcProxyServer()) << "Client data available" << proxyClient << qUtf8Printable(data);
|
||||||
|
if (proxyClient->tunnelConnected()) {
|
||||||
|
// Pipe the data
|
||||||
|
} else {
|
||||||
|
// Pipe data into json rpc server
|
||||||
|
m_jsonRpcServer->processData(proxyClient, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyServer::startServer()
|
void ProxyServer::startServer()
|
||||||
@ -60,3 +100,5 @@ void ProxyServer::stopServer()
|
|||||||
interface->stopServer();
|
interface->stopServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -2,22 +2,32 @@
|
|||||||
#define PROXYSERVER_H
|
#define PROXYSERVER_H
|
||||||
|
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
#include <QHash>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "proxyclient.h"
|
#include "proxyclient.h"
|
||||||
|
#include "jsonrpcserver.h"
|
||||||
#include "transportinterface.h"
|
#include "transportinterface.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class ProxyServer : public QObject
|
class ProxyServer : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ProxyServer(QObject *parent = nullptr);
|
explicit ProxyServer(QObject *parent = nullptr);
|
||||||
|
~ProxyServer();
|
||||||
|
|
||||||
void registerTransportInterface(TransportInterface *interface);
|
void registerTransportInterface(TransportInterface *interface);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
JsonRpcServer *m_jsonRpcServer = nullptr;
|
||||||
QList<TransportInterface *> m_transportInterfaces;
|
QList<TransportInterface *> m_transportInterfaces;
|
||||||
QList<QUuid> m_unauthenticatedClients;
|
|
||||||
|
QHash<QUuid, ProxyClient *> m_proxyClients;
|
||||||
|
QHash<ProxyClient *, ProxyClient *> m_tunnels;
|
||||||
|
|
||||||
|
void sendResponse(TransportInterface *interface, const QUuid &clientId, const QVariantMap &response = QVariantMap());
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onClientConnected(const QUuid &clientId);
|
void onClientConnected(const QUuid &clientId);
|
||||||
@ -30,4 +40,6 @@ public slots:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // PROXYSERVER_H
|
#endif // PROXYSERVER_H
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#include "transportinterface.h"
|
#include "transportinterface.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
TransportInterface::TransportInterface(QObject *parent) :
|
TransportInterface::TransportInterface(QObject *parent) :
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
{
|
{
|
||||||
@ -15,3 +17,5 @@ TransportInterface::~TransportInterface()
|
|||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class TransportInterface : public QObject
|
class TransportInterface : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -15,6 +17,8 @@ public:
|
|||||||
virtual void sendData(const QUuid &clientId, const QByteArray &data) = 0;
|
virtual void sendData(const QUuid &clientId, const QByteArray &data) = 0;
|
||||||
virtual void sendData(const QList<QUuid> &clients, const QByteArray &data) = 0;
|
virtual void sendData(const QList<QUuid> &clients, const QByteArray &data) = 0;
|
||||||
|
|
||||||
|
virtual void killClientConnection(const QUuid &clientId) = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void clientConnected(const QUuid &clientId);
|
void clientConnected(const QUuid &clientId);
|
||||||
void clientDisconnected(const QUuid &clientId);
|
void clientDisconnected(const QUuid &clientId);
|
||||||
@ -29,4 +33,6 @@ public slots:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // TRANSPORTINTERFACE_H
|
#endif // TRANSPORTINTERFACE_H
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
WebSocketServer::WebSocketServer(const QSslConfiguration &sslConfiguration, QObject *parent) :
|
WebSocketServer::WebSocketServer(const QSslConfiguration &sslConfiguration, QObject *parent) :
|
||||||
TransportInterface(parent),
|
TransportInterface(parent),
|
||||||
m_sslConfiguration(sslConfiguration)
|
m_sslConfiguration(sslConfiguration)
|
||||||
@ -54,6 +56,16 @@ void WebSocketServer::sendData(const QList<QUuid> &clients, const QByteArray &da
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebSocketServer::killClientConnection(const QUuid &clientId)
|
||||||
|
{
|
||||||
|
QWebSocket *client = m_clientList.value(clientId);
|
||||||
|
if (!client)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qCWarning(dcWebSocketServer()) << "Kill client connection" << clientId.toString();
|
||||||
|
client->close(QWebSocketProtocol::CloseCodeBadOperation);
|
||||||
|
}
|
||||||
|
|
||||||
void WebSocketServer::onClientConnected()
|
void WebSocketServer::onClientConnected()
|
||||||
{
|
{
|
||||||
// Got a new client connected
|
// Got a new client connected
|
||||||
@ -69,7 +81,7 @@ void WebSocketServer::onClientConnected()
|
|||||||
|
|
||||||
// Create new uuid for this connection
|
// Create new uuid for this connection
|
||||||
QUuid clientId = QUuid::createUuid();
|
QUuid clientId = QUuid::createUuid();
|
||||||
qCDebug(dcWebSocketServer()) << "New client connected:" << client->peerAddress().toString() << clientId;
|
qCDebug(dcWebSocketServer()) << "New client connected:" << client << client->peerAddress().toString() << clientId.toString();
|
||||||
|
|
||||||
// Append the new client to the client list
|
// Append the new client to the client list
|
||||||
m_clientList.insert(clientId, client);
|
m_clientList.insert(clientId, client);
|
||||||
@ -88,7 +100,7 @@ void WebSocketServer::onClientDisconnected()
|
|||||||
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
||||||
QUuid clientId = m_clientList.key(client);
|
QUuid clientId = m_clientList.key(client);
|
||||||
|
|
||||||
qCDebug(dcWebSocketServer()) << "Client disconnected:" << client->peerAddress().toString() << clientId;
|
qCDebug(dcWebSocketServer()) << "Client disconnected:" << client << client->peerAddress().toString() << clientId.toString();
|
||||||
|
|
||||||
m_clientList.take(clientId)->deleteLater();
|
m_clientList.take(clientId)->deleteLater();
|
||||||
emit clientDisconnected(clientId);
|
emit clientDisconnected(clientId);
|
||||||
@ -159,3 +171,5 @@ bool WebSocketServer::stopServer()
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include "transportinterface.h"
|
#include "transportinterface.h"
|
||||||
|
|
||||||
|
namespace remoteproxy {
|
||||||
|
|
||||||
class WebSocketServer : public TransportInterface
|
class WebSocketServer : public TransportInterface
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -27,6 +29,8 @@ public:
|
|||||||
void sendData(const QUuid &clientId, const QByteArray &data) override;
|
void sendData(const QUuid &clientId, const QByteArray &data) override;
|
||||||
void sendData(const QList<QUuid> &clients, const QByteArray &data) override;
|
void sendData(const QList<QUuid> &clients, const QByteArray &data) override;
|
||||||
|
|
||||||
|
void killClientConnection(const QUuid &clientId) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUrl m_serverUrl;
|
QUrl m_serverUrl;
|
||||||
QWebSocketServer *m_server = nullptr;
|
QWebSocketServer *m_server = nullptr;
|
||||||
@ -50,4 +54,6 @@ public slots:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // WEBSOCKETSERVER_H
|
#endif // WEBSOCKETSERVER_H
|
||||||
|
|||||||
6
libnymea-remoteproxyclient/jsonrpcclient.cpp
Normal file
6
libnymea-remoteproxyclient/jsonrpcclient.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include "jsonrpcclient.h"
|
||||||
|
|
||||||
|
JsonRpcClient::JsonRpcClient(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
17
libnymea-remoteproxyclient/jsonrpcclient.h
Normal file
17
libnymea-remoteproxyclient/jsonrpcclient.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef JSONRPCCLIENT_H
|
||||||
|
#define JSONRPCCLIENT_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class JsonRpcClient : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit JsonRpcClient(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // JSONRPCCLIENT_H
|
||||||
@ -5,12 +5,16 @@ TARGET = nymea-remoteproxyclient
|
|||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
remoteproxyconnector.h \
|
remoteproxyconnector.h \
|
||||||
websocketconnector.h
|
websocketconnector.h \
|
||||||
|
socketconnector.h \
|
||||||
|
jsonrpcclient.h
|
||||||
|
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
remoteproxyconnector.cpp \
|
remoteproxyconnector.cpp \
|
||||||
websocketconnector.cpp
|
websocketconnector.cpp \
|
||||||
|
socketconnector.cpp \
|
||||||
|
jsonrpcclient.cpp
|
||||||
|
|
||||||
|
|
||||||
# install header file with relative subdirectory
|
# install header file with relative subdirectory
|
||||||
|
|||||||
@ -119,6 +119,12 @@ bool RemoteProxyConnector::sendData(const QByteArray &data)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isConnected()) {
|
||||||
|
qCWarning(dcRemoteProxyConnector()) << "Not connected";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(dcRemoteProxyConnector()) << "Sending data:" << data;
|
||||||
qint64 dataSendCount = m_webSocket->sendTextMessage(QString::fromUtf8(data));
|
qint64 dataSendCount = m_webSocket->sendTextMessage(QString::fromUtf8(data));
|
||||||
if (dataSendCount != data.count()) {
|
if (dataSendCount != data.count()) {
|
||||||
qCWarning(dcRemoteProxyConnector()) << "Could not send all data to" << serverUrl().toString();
|
qCWarning(dcRemoteProxyConnector()) << "Could not send all data to" << serverUrl().toString();
|
||||||
|
|||||||
11
libnymea-remoteproxyclient/socketconnector.cpp
Normal file
11
libnymea-remoteproxyclient/socketconnector.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "socketconnector.h"
|
||||||
|
|
||||||
|
SocketConnector::SocketConnector(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketConnector::~SocketConnector()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
29
libnymea-remoteproxyclient/socketconnector.h
Normal file
29
libnymea-remoteproxyclient/socketconnector.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef SOCKETCONNECTOR_H
|
||||||
|
#define SOCKETCONNECTOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
|
class SocketConnector : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SocketConnector(QObject *parent = nullptr);
|
||||||
|
virtual ~SocketConnector() = 0;
|
||||||
|
|
||||||
|
virtual void sendData(const QByteArray &data) = 0;
|
||||||
|
virtual bool isConnected() = 0;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void connected();
|
||||||
|
void disconnected();
|
||||||
|
|
||||||
|
void dataReceived(const QByteArray &data);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
virtual void connectServer(const QHostAddress &address, quint16 port) = 0;
|
||||||
|
virtual void disconnectServer() = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SOCKETCONNECTOR_H
|
||||||
@ -1,6 +1,12 @@
|
|||||||
#include "websocketconnector.h"
|
#include "websocketconnector.h"
|
||||||
|
|
||||||
WebSocketConnector::WebSocketConnector(QObject *parent) : QObject(parent)
|
WebSocketConnector::WebSocketConnector(QObject *parent) :
|
||||||
|
SocketConnector(parent)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebSocketConnector::sendData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
Q_UNUSED(data)
|
||||||
|
}
|
||||||
|
|||||||
@ -3,15 +3,19 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class WebSocketConnector : public QObject
|
#include "socketconnector.h"
|
||||||
|
|
||||||
|
class WebSocketConnector : public SocketConnector
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit WebSocketConnector(QObject *parent = nullptr);
|
explicit WebSocketConnector(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void sendData(const QByteArray &data) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WEBSOCKETCONNECTOR_H
|
#endif // WEBSOCKETCONNECTOR_H
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
#include "authentication/awsauthenticator.h"
|
#include "authentication/awsauthenticator.h"
|
||||||
|
|
||||||
|
using namespace remoteproxy;
|
||||||
|
|
||||||
static QHash<QString, bool> s_loggingFilters;
|
static QHash<QString, bool> s_loggingFilters;
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +1,59 @@
|
|||||||
|
#include "proxyclient.h"
|
||||||
#include "mockauthenticator.h"
|
#include "mockauthenticator.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
#include "authentication/authenticationreply.h"
|
#include "authentication/authenticationreply.h"
|
||||||
|
|
||||||
|
|
||||||
MockAuthenticator::MockAuthenticator(QObject *parent) :
|
MockAuthenticator::MockAuthenticator(QObject *parent) :
|
||||||
Authenticator(parent)
|
Authenticator(parent)
|
||||||
{
|
{
|
||||||
m_timer = new QTimer(this);
|
|
||||||
m_timer->setSingleShot(true);
|
|
||||||
|
|
||||||
connect(m_timer, &QTimer::timeout, this, &MockAuthenticator::onTimeout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MockAuthenticator::onTimeout()
|
QString MockAuthenticator::name() const
|
||||||
{
|
{
|
||||||
|
return "Mock authenticator";
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticationReply *MockAuthenticator::authenticate(const QUuid &clientId, const QString &token)
|
void MockAuthenticator::setTimeoutDuration(int timeout)
|
||||||
{
|
{
|
||||||
qCDebug(dcAuthenticator()) << "MockAuthenticator: Start authentication for" << clientId << "using token" << token;
|
m_timeoutDuration = timeout;
|
||||||
AuthenticationReply *reply = new AuthenticationReply(clientId, token, this);
|
|
||||||
return reply;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MockAuthenticator::setExpectedAuthenticationError(Authenticator::AuthenticationError error)
|
||||||
|
{
|
||||||
|
m_expectedError = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MockAuthenticator::replyFinished()
|
||||||
|
{
|
||||||
|
MockAuthenticationReply *reply = static_cast<MockAuthenticationReply *>(sender());
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
qCDebug(dcAuthenticator()) << name() << "Authentication finished.";
|
||||||
|
|
||||||
|
setReplyError(reply->authenticationReply(), reply->error());
|
||||||
|
setReplyFinished(reply->authenticationReply());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AuthenticationReply *MockAuthenticator::authenticate(ProxyClient *proxyClient)
|
||||||
|
{
|
||||||
|
qCDebug(dcAuthenticator()) << name() << "Start authentication for" << proxyClient << "using token" << proxyClient->token();
|
||||||
|
|
||||||
|
AuthenticationReply *authenticationReply = createAuthenticationReply(proxyClient, this);
|
||||||
|
|
||||||
|
MockAuthenticationReply *reply = new MockAuthenticationReply(m_timeoutDuration, m_expectedError, authenticationReply, this);
|
||||||
|
connect(reply, &MockAuthenticationReply::finished, this, &MockAuthenticator::replyFinished);
|
||||||
|
|
||||||
|
return authenticationReply;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockAuthenticationReply::MockAuthenticationReply(int timeout, Authenticator::AuthenticationError error, AuthenticationReply *authenticationReply, QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_error(error),
|
||||||
|
m_authenticationReply(authenticationReply)
|
||||||
|
|
||||||
|
{
|
||||||
|
QTimer::singleShot(timeout, this, &MockAuthenticationReply::finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,25 +6,47 @@
|
|||||||
|
|
||||||
#include "authentication/authenticator.h"
|
#include "authentication/authenticator.h"
|
||||||
|
|
||||||
|
using namespace remoteproxy;
|
||||||
|
|
||||||
|
class MockAuthenticationReply : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit MockAuthenticationReply(int timeout, Authenticator::AuthenticationError error, AuthenticationReply *authenticationReply, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
AuthenticationReply *authenticationReply() const { return m_authenticationReply; }
|
||||||
|
Authenticator::AuthenticationError error() const { return m_error; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Authenticator::AuthenticationError m_error;
|
||||||
|
AuthenticationReply *m_authenticationReply;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class MockAuthenticator : public Authenticator
|
class MockAuthenticator : public Authenticator
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit MockAuthenticator(QObject *parent = nullptr);
|
explicit MockAuthenticator(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString name() const override;
|
||||||
|
|
||||||
void setTimeoutDuration(int timeout);
|
void setTimeoutDuration(int timeout);
|
||||||
void setExpectedAuthenticationError(Authenticator::AuthenticationError error);
|
void setExpectedAuthenticationError(Authenticator::AuthenticationError error = AuthenticationErrorNoError);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTimer * m_timer = nullptr;
|
|
||||||
int m_timeoutDuration = 1000;
|
int m_timeoutDuration = 1000;
|
||||||
Authenticator::AuthenticationError m_expectedError;
|
Authenticator::AuthenticationError m_expectedError;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onTimeout();
|
void replyFinished();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
AuthenticationReply *authenticate(const QUuid &clientId, const QString &token) override;
|
AuthenticationReply *authenticate(ProxyClient *proxyClient) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MOCKAUTHENTICATOR_H
|
#endif // MOCKAUTHENTICATOR_H
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QSignalSpy>
|
#include <QSignalSpy>
|
||||||
#include <QWebSocket>
|
#include <QWebSocket>
|
||||||
|
#include <QJsonDocument>
|
||||||
#include <QWebSocketServer>
|
#include <QWebSocketServer>
|
||||||
|
|
||||||
RemoteProxyTests::RemoteProxyTests(QObject *parent) :
|
RemoteProxyTests::RemoteProxyTests(QObject *parent) :
|
||||||
@ -40,6 +41,7 @@ RemoteProxyTests::RemoteProxyTests(QObject *parent) :
|
|||||||
void RemoteProxyTests::cleanUpEngine()
|
void RemoteProxyTests::cleanUpEngine()
|
||||||
{
|
{
|
||||||
if (Engine::exists()) {
|
if (Engine::exists()) {
|
||||||
|
Engine::instance()->stop();
|
||||||
Engine::instance()->destroy();
|
Engine::instance()->destroy();
|
||||||
QVERIFY(!Engine::exists());
|
QVERIFY(!Engine::exists());
|
||||||
}
|
}
|
||||||
@ -48,16 +50,22 @@ void RemoteProxyTests::cleanUpEngine()
|
|||||||
void RemoteProxyTests::restartEngine()
|
void RemoteProxyTests::restartEngine()
|
||||||
{
|
{
|
||||||
cleanUpEngine();
|
cleanUpEngine();
|
||||||
|
startEngine();
|
||||||
QVERIFY(Engine::instance() != nullptr);
|
|
||||||
QVERIFY(Engine::exists());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteProxyTests::startEngine()
|
void RemoteProxyTests::startEngine()
|
||||||
{
|
{
|
||||||
if (!Engine::exists()) {
|
if (!Engine::exists()) {
|
||||||
Engine::instance();
|
QString serverName = "nymea-remoteproxy-testserver";
|
||||||
|
Engine::instance()->setAuthenticator(m_authenticator);
|
||||||
|
Engine::instance()->setAuthenticationServerUrl(QUrl("https://localhost"));
|
||||||
|
Engine::instance()->setServerName(serverName);
|
||||||
|
Engine::instance()->setWebSocketServerPort(m_port);
|
||||||
|
Engine::instance()->setWebSocketServerHostAddress(QHostAddress::LocalHost);
|
||||||
|
Engine::instance()->setSslConfiguration(m_sslConfiguration);
|
||||||
|
|
||||||
QVERIFY(Engine::exists());
|
QVERIFY(Engine::exists());
|
||||||
|
QVERIFY(Engine::instance()->serverName() == serverName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,16 +73,7 @@ void RemoteProxyTests::startServer()
|
|||||||
{
|
{
|
||||||
startEngine();
|
startEngine();
|
||||||
|
|
||||||
QString serverName = "nymea-remoteproxy-testserver";
|
|
||||||
Engine::instance()->setAuthenticationServerUrl(QUrl("https://localhost"));
|
|
||||||
Engine::instance()->setServerName(serverName);
|
|
||||||
Engine::instance()->setAuthenticator(m_authenticator);
|
|
||||||
Engine::instance()->setWebSocketServerPort(m_port);
|
|
||||||
Engine::instance()->setWebSocketServerHostAddress(QHostAddress::LocalHost);
|
|
||||||
Engine::instance()->setSslConfiguration(m_sslConfiguration);
|
|
||||||
Engine::instance()->start();
|
Engine::instance()->start();
|
||||||
|
|
||||||
QVERIFY(Engine::instance()->serverName() == serverName);
|
|
||||||
QVERIFY(Engine::instance()->running());
|
QVERIFY(Engine::instance()->running());
|
||||||
QVERIFY(Engine::instance()->webSocketServer()->running());
|
QVERIFY(Engine::instance()->webSocketServer()->running());
|
||||||
}
|
}
|
||||||
@ -88,6 +87,99 @@ void RemoteProxyTests::stopServer()
|
|||||||
QVERIFY(!Engine::instance()->running());
|
QVERIFY(!Engine::instance()->running());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant RemoteProxyTests::invokeApiCall(const QString &method, const QVariantMap params, bool remainsConnected)
|
||||||
|
{
|
||||||
|
Q_UNUSED(remainsConnected)
|
||||||
|
|
||||||
|
QVariantMap request;
|
||||||
|
request.insert("id", m_commandCounter);
|
||||||
|
request.insert("method", method);
|
||||||
|
request.insert("params", params);
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromVariant(request);
|
||||||
|
|
||||||
|
QWebSocket *socket = new QWebSocket("proxy-testclient", QWebSocketProtocol::Version13);
|
||||||
|
connect(socket, &QWebSocket::sslErrors, this, &RemoteProxyTests::sslErrors);
|
||||||
|
QSignalSpy spyConnection(socket, SIGNAL(connected()));
|
||||||
|
socket->open(Engine::instance()->webSocketServer()->serverUrl());
|
||||||
|
spyConnection.wait();
|
||||||
|
if (spyConnection.count() == 0) {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
// QSignalSpy disconnectedSpy(socket, SIGNAL(disconnected()));
|
||||||
|
QSignalSpy dataSpy(socket, SIGNAL(textMessageReceived(QString)));
|
||||||
|
socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact)));
|
||||||
|
dataSpy.wait();
|
||||||
|
// if (remainsConnected) {
|
||||||
|
// disconnectedSpy.wait(1000);
|
||||||
|
// if (socket->state() != QAbstractSocket::UnconnectedState) {
|
||||||
|
// qWarning() << "!!!!!!!!!!!!! socket still connected but should be disconnected!";
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// disconnectedSpy.wait();
|
||||||
|
// if (socket->state() != QAbstractSocket::ConnectedState) {
|
||||||
|
// qWarning() << "!!!!!!!!!!!!! socket not connected but should be!";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
socket->close();
|
||||||
|
socket->deleteLater();
|
||||||
|
|
||||||
|
for (int i = 0; i < dataSpy.count(); i++) {
|
||||||
|
// Make sure the response it a valid JSON string
|
||||||
|
QJsonParseError error;
|
||||||
|
jsonDoc = QJsonDocument::fromJson(dataSpy.at(i).last().toByteArray(), &error);
|
||||||
|
if (error.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "JSON parser error" << error.errorString();
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
QVariantMap response = jsonDoc.toVariant().toMap();
|
||||||
|
|
||||||
|
// skip notifications
|
||||||
|
if (response.contains("notification"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (response.value("id").toInt() == m_commandCounter) {
|
||||||
|
m_commandCounter++;
|
||||||
|
return jsonDoc.toVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_commandCounter++;
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant RemoteProxyTests::injectSocketData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
QWebSocket *socket = new QWebSocket("proxy-testclient", QWebSocketProtocol::Version13);
|
||||||
|
connect(socket, &QWebSocket::sslErrors, this, &RemoteProxyTests::sslErrors);
|
||||||
|
QSignalSpy spyConnection(socket, SIGNAL(connected()));
|
||||||
|
socket->open(Engine::instance()->webSocketServer()->serverUrl());
|
||||||
|
spyConnection.wait();
|
||||||
|
if (spyConnection.count() == 0) {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString)));
|
||||||
|
socket->sendTextMessage(QString(data));
|
||||||
|
spy.wait();
|
||||||
|
|
||||||
|
socket->close();
|
||||||
|
socket->deleteLater();
|
||||||
|
|
||||||
|
for (int i = 0; i < spy.count(); i++) {
|
||||||
|
// Make sure the response it a valid JSON string
|
||||||
|
QJsonParseError error;
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.at(i).last().toByteArray(), &error);
|
||||||
|
if (error.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "JSON parser error" << error.errorString();
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
return jsonDoc.toVariant();
|
||||||
|
}
|
||||||
|
m_commandCounter++;
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
void RemoteProxyTests::initTestCase()
|
void RemoteProxyTests::initTestCase()
|
||||||
{
|
{
|
||||||
qRegisterMetaType<RemoteProxyConnector::Error>();
|
qRegisterMetaType<RemoteProxyConnector::Error>();
|
||||||
@ -104,10 +196,8 @@ void RemoteProxyTests::cleanupTestCase()
|
|||||||
|
|
||||||
void RemoteProxyTests::startStopServer()
|
void RemoteProxyTests::startStopServer()
|
||||||
{
|
{
|
||||||
startEngine();
|
|
||||||
startServer();
|
startServer();
|
||||||
stopServer();
|
stopServer();
|
||||||
cleanUpEngine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteProxyTests::webserverConnectionBlocked()
|
void RemoteProxyTests::webserverConnectionBlocked()
|
||||||
@ -134,7 +224,6 @@ void RemoteProxyTests::webserverConnectionBlocked()
|
|||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
stopServer();
|
stopServer();
|
||||||
cleanUpEngine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteProxyTests::webserverSocketVersion()
|
void RemoteProxyTests::webserverSocketVersion()
|
||||||
@ -153,20 +242,11 @@ void RemoteProxyTests::webserverSocketVersion()
|
|||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
stopServer();
|
stopServer();
|
||||||
cleanUpEngine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteProxyTests::webserverConnection()
|
void RemoteProxyTests::webserverConnection()
|
||||||
{
|
{
|
||||||
// Start the server
|
|
||||||
startServer();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
stopServer();
|
|
||||||
cleanUpEngine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteProxyTests::sslConfigurations()
|
void RemoteProxyTests::sslConfigurations()
|
||||||
@ -199,25 +279,143 @@ void RemoteProxyTests::sslConfigurations()
|
|||||||
QVERIFY(!connector->isConnected());
|
QVERIFY(!connector->isConnected());
|
||||||
|
|
||||||
connector->deleteLater();
|
connector->deleteLater();
|
||||||
Engine::instance()->stop();
|
stopServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteProxyTests::getIntrospect()
|
||||||
|
{
|
||||||
|
// Start the server
|
||||||
|
startServer();
|
||||||
|
|
||||||
|
QVariant response = invokeApiCall("RemoteProxy.Introspect");
|
||||||
|
qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
stopServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteProxyTests::getHello()
|
||||||
|
{
|
||||||
|
// Start the server
|
||||||
|
startServer();
|
||||||
|
|
||||||
|
QVariant response = invokeApiCall("RemoteProxy.Hello");
|
||||||
|
qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
stopServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteProxyTests::apiBasicCalls_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("data");
|
||||||
|
QTest::addColumn<bool>("valid");
|
||||||
|
|
||||||
|
QTest::newRow("valid call") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\"}") << true;
|
||||||
|
QTest::newRow("missing id") << QByteArray("{\"method\":\"RemoteProxy.Hello\"}")<< false;
|
||||||
|
QTest::newRow("missing method") << QByteArray("{\"id\":42}") << false;
|
||||||
|
QTest::newRow("borked") << QByteArray("{\"id\":42, \"method\":\"RemoteProx")<< false;
|
||||||
|
QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Explode\"}") << false;
|
||||||
|
QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"ProxyRemote.Hello\"}") << false;
|
||||||
|
QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"RemoteProxyHello\"}") << false;
|
||||||
|
QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\", \"params\":{\"törööö\":\"chooo-chooo\"}}") << false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteProxyTests::apiBasicCalls()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, data);
|
||||||
|
QFETCH(bool, valid);
|
||||||
|
|
||||||
|
// Start the server
|
||||||
|
startServer();
|
||||||
|
|
||||||
|
QVariant response = injectSocketData(data);
|
||||||
|
if (valid) {
|
||||||
|
QVERIFY2(response.toMap().value("status").toString() == "success", "Call wasn't parsed correctly by nymea.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
stopServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteProxyTests::authenticate_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("uuid");
|
||||||
|
QTest::addColumn<QString>("name");
|
||||||
|
QTest::addColumn<QString>("token");
|
||||||
|
|
||||||
|
QTest::addColumn<int>("timeout");
|
||||||
|
QTest::addColumn<Authenticator::AuthenticationError>("expectedError");
|
||||||
|
|
||||||
|
QTest::newRow("success") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << "Hh5JrkdFVstVVyCnfE3vVT3zG_JTacXEhPLZhCei"
|
||||||
|
<< 100 << Authenticator::AuthenticationErrorNoError;
|
||||||
|
|
||||||
|
QTest::newRow("failed") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << "invalid_token_42"
|
||||||
|
<< 100 << Authenticator::AuthenticationErrorAuthenticationFailed;
|
||||||
|
|
||||||
|
QTest::newRow("not responding") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << "invalid_token_42"
|
||||||
|
<< 200 << Authenticator::AuthenticationErrorAuthenticationServerNotResponding;
|
||||||
|
|
||||||
|
QTest::newRow("aborted") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << "invalid_token_42"
|
||||||
|
<< 100 << Authenticator::AuthenticationErrorAborted;
|
||||||
|
|
||||||
|
QTest::newRow("unknown") << QUuid::createUuid().toString() << "Testclient, hello form the test!" << "invalid_token_42"
|
||||||
|
<< 100 << Authenticator::AuthenticationErrorUnknown;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteProxyTests::authenticate()
|
void RemoteProxyTests::authenticate()
|
||||||
{
|
{
|
||||||
// // Start the server
|
QFETCH(QString, uuid);
|
||||||
// startServer();
|
QFETCH(QString, name);
|
||||||
|
QFETCH(QString, token);
|
||||||
|
QFETCH(int, timeout);
|
||||||
|
QFETCH(Authenticator::AuthenticationError, expectedError);
|
||||||
|
|
||||||
// // Connect to the server
|
// Start the server
|
||||||
// RemoteProxyConnector *connector = new RemoteProxyConnector(this);
|
startServer();
|
||||||
// connector->setInsecureConnection(true);
|
|
||||||
|
|
||||||
// QSignalSpy spy(connector, &RemoteProxyConnector::connected);
|
// Configure result
|
||||||
// connector->connectServer(QHostAddress::LocalHost, m_port);
|
m_authenticator->setExpectedAuthenticationError(expectedError);
|
||||||
// spy.wait();
|
m_authenticator->setTimeoutDuration(timeout);
|
||||||
|
|
||||||
// connector->disconnectServer();
|
// Create request
|
||||||
// connector->deleteLater();
|
QVariantMap params;
|
||||||
// Engine::instance()->stop();
|
params.insert("uuid", uuid);
|
||||||
|
params.insert("name", name);
|
||||||
|
params.insert("token", token);
|
||||||
|
|
||||||
|
QVariant response = invokeApiCall("Authentication.Authenticate", params);
|
||||||
|
qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
|
||||||
|
verifyAuthenticationError(response, expectedError);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
stopServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteProxyTests::timeout()
|
||||||
|
{
|
||||||
|
// Start the server
|
||||||
|
startServer();
|
||||||
|
|
||||||
|
// Configure result
|
||||||
|
// Start the server
|
||||||
|
startServer();
|
||||||
|
|
||||||
|
// Configure result
|
||||||
|
m_authenticator->setExpectedAuthenticationError();
|
||||||
|
m_authenticator->setTimeoutDuration(6000);
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
QVariantMap params;
|
||||||
|
params.insert("uuid", "uuid");
|
||||||
|
params.insert("name", "name");
|
||||||
|
params.insert("token", "token");
|
||||||
|
|
||||||
|
QVariant response = invokeApiCall("Authentication.Authenticate", params);
|
||||||
|
qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented));
|
||||||
|
// Clean up
|
||||||
|
stopServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -5,12 +5,16 @@
|
|||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
#include <QSslKey>
|
#include <QSslKey>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QWebSocket>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QSslCertificate>
|
#include <QSslCertificate>
|
||||||
#include <QSslConfiguration>
|
#include <QSslConfiguration>
|
||||||
|
|
||||||
|
#include "jsonrpc/jsontypes.h"
|
||||||
#include "mockauthenticator.h"
|
#include "mockauthenticator.h"
|
||||||
|
|
||||||
|
using namespace remoteproxy;
|
||||||
|
|
||||||
class RemoteProxyTests : public QObject
|
class RemoteProxyTests : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -23,25 +27,71 @@ private:
|
|||||||
QSslConfiguration m_sslConfiguration;
|
QSslConfiguration m_sslConfiguration;
|
||||||
MockAuthenticator *m_authenticator = nullptr;
|
MockAuthenticator *m_authenticator = nullptr;
|
||||||
|
|
||||||
|
int m_commandCounter = 0;
|
||||||
|
|
||||||
void cleanUpEngine();
|
void cleanUpEngine();
|
||||||
void restartEngine();
|
void restartEngine();
|
||||||
void startEngine();
|
void startEngine();
|
||||||
void startServer();
|
void startServer();
|
||||||
void stopServer();
|
void stopServer();
|
||||||
|
|
||||||
|
QVariant invokeApiCall(const QString &method, const QVariantMap params = QVariantMap(), bool remainsConnected = true);
|
||||||
|
QVariant injectSocketData(const QByteArray &data);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
void cleanupTestCase();
|
void cleanupTestCase();
|
||||||
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
// Basic stuff
|
||||||
void startStopServer();
|
void startStopServer();
|
||||||
|
|
||||||
|
// WebSocket connection
|
||||||
void webserverConnectionBlocked();
|
void webserverConnectionBlocked();
|
||||||
void webserverSocketVersion();
|
void webserverSocketVersion();
|
||||||
void webserverConnection();
|
void webserverConnection();
|
||||||
void sslConfigurations();
|
void sslConfigurations();
|
||||||
|
|
||||||
|
// Api
|
||||||
|
void getIntrospect();
|
||||||
|
void getHello();
|
||||||
|
|
||||||
|
void apiBasicCalls_data();
|
||||||
|
void apiBasicCalls();
|
||||||
|
|
||||||
|
void authenticate_data();
|
||||||
void authenticate();
|
void authenticate();
|
||||||
|
|
||||||
|
void timeout();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void sslErrors(const QList<QSslError> &) {
|
||||||
|
QWebSocket *socket = static_cast<QWebSocket*>(sender());
|
||||||
|
socket->ignoreSslErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void verifyError(const QVariant &response, const QString &fieldName, const QString &error)
|
||||||
|
{
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromVariant(response);
|
||||||
|
QVERIFY2(response.toMap().value("status").toString() == QString("success"),
|
||||||
|
QString("\nExpected status: \"success\"\nGot: %2\nFull message: %3")
|
||||||
|
.arg(response.toMap().value("status").toString())
|
||||||
|
.arg(jsonDoc.toJson().data())
|
||||||
|
.toLatin1().data());
|
||||||
|
QVERIFY2(response.toMap().value("params").toMap().value(fieldName).toString() == error,
|
||||||
|
QString("\nExpected: %1\nGot: %2\nFull message: %3\n")
|
||||||
|
.arg(error)
|
||||||
|
.arg(response.toMap().value("params").toMap().value(fieldName).toString())
|
||||||
|
.arg(jsonDoc.toJson().data())
|
||||||
|
.toLatin1().data());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void verifyAuthenticationError(const QVariant &response, Authenticator::AuthenticationError error = Authenticator::AuthenticationErrorNoError) {
|
||||||
|
verifyError(response, "authenticationError", JsonTypes::authenticationErrorToString(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NYMEA_REMOTEPROXY_TESTS_H
|
#endif // NYMEA_REMOTEPROXY_TESTS_H
|
||||||
|
|||||||
Reference in New Issue
Block a user