Add server and client registration and prepare client data multiplexing

This commit is contained in:
Simon Stürz 2021-07-29 19:04:44 +02:00
parent 3174387e96
commit 56d32bed2b
28 changed files with 837 additions and 257 deletions

View File

@ -101,7 +101,7 @@ void Engine::start(ProxyConfiguration *configuration)
// Tunnel proxy
// -------------------------------------
m_tunnelProxyManager = new TunnelProxyManager(this);
m_tunnelProxyServer = new TunnelProxyServer(this);
m_webSocketServerTunnelProxy = new WebSocketServer(m_configuration->sslEnabled(), m_configuration->sslConfiguration(), this);
m_tcpSocketServerTunnelProxy = new TcpSocketServer(m_configuration->sslEnabled(), m_configuration->sslConfiguration(), this);
@ -120,12 +120,12 @@ void Engine::start(ProxyConfiguration *configuration)
m_tcpSocketServerTunnelProxy->setServerUrl(tcpSocketServerTunnelProxyUrl);
// Register the transport interfaces in the proxy server
m_tunnelProxyManager->registerTransportInterface(m_webSocketServerTunnelProxy);
m_tunnelProxyManager->registerTransportInterface(m_tcpSocketServerTunnelProxy);
m_tunnelProxyServer->registerTransportInterface(m_webSocketServerTunnelProxy);
m_tunnelProxyServer->registerTransportInterface(m_tcpSocketServerTunnelProxy);
// Start the server
qCDebug(dcEngine()) << "Starting the tunnel proxy manager...";
m_tunnelProxyManager->startServer();
m_tunnelProxyServer->startServer();
// Start the monitor server
m_monitorServer = new MonitorServer(configuration->monitorSocketFileName(), this);
@ -195,9 +195,9 @@ ProxyServer *Engine::proxyServer() const
return m_proxyServer;
}
TunnelProxyManager *Engine::tunnelProxyManager() const
TunnelProxyServer *Engine::tunnelProxyServer() const
{
return m_tunnelProxyManager;
return m_tunnelProxyServer;
}
TcpSocketServer *Engine::tcpSocketServerProxy() const

View File

@ -43,7 +43,7 @@
#include "server/tcpsocketserver.h"
#include "server/websocketserver.h"
#include "authentication/authenticator.h"
#include "tunnelproxy/tunnelproxymanager.h"
#include "tunnelproxy/tunnelproxyserver.h"
namespace remoteproxy {
@ -71,7 +71,7 @@ public:
Authenticator *authenticator() const;
ProxyServer *proxyServer() const;
TunnelProxyManager *tunnelProxyManager() const;
TunnelProxyServer *tunnelProxyServer() const;
TcpSocketServer *tcpSocketServerProxy() const;
WebSocketServer *webSocketServerProxy() const;
@ -101,7 +101,7 @@ private:
Authenticator *m_authenticator = nullptr;
ProxyServer *m_proxyServer = nullptr;
TunnelProxyManager *m_tunnelProxyManager = nullptr;
TunnelProxyServer *m_tunnelProxyServer = nullptr;
TcpSocketServer *m_tcpSocketServerProxy = nullptr;
WebSocketServer *m_webSocketServerProxy = nullptr;

View File

@ -63,7 +63,7 @@ QString AuthenticationHandler::name() const
JsonReply *AuthenticationHandler::Authenticate(const QVariantMap &params, TransportClient *transportClient)
{
QString uuid = params.value("uuid").toString();
QUuid uuid = params.value("uuid").toUuid();
QString name = params.value("name").toString();
QString token = params.value("token").toString();
QString nonce = params.value("nonce").toString();

View File

@ -62,7 +62,7 @@ void JsonTypes::init()
// Declare types
s_basicType = enumToStrings(JsonTypes::staticMetaObject, "BasicType");
s_authenticationError = enumToStrings(Authenticator::staticMetaObject, "AuthenticationError");
s_tunnelProxyError = enumToStrings(TunnelProxyManager::staticMetaObject, "Error");
s_tunnelProxyError = enumToStrings(TunnelProxyServer::staticMetaObject, "TunnelProxyError");
s_initialized = true;
}

View File

@ -33,7 +33,7 @@
#include <QMetaEnum>
#include <QStringList>
#include "tunnelproxy/tunnelproxymanager.h"
#include "tunnelproxy/tunnelproxyserver.h"
#include "authentication/authenticator.h"
namespace remoteproxy {
@ -91,7 +91,7 @@ public:
// Declare types
DECLARE_TYPE(basicType, "BasicType", JsonTypes, BasicType)
DECLARE_TYPE(authenticationError, "AuthenticationError", Authenticator, AuthenticationError)
DECLARE_TYPE(tunnelProxyError, "TunnelProxyError", TunnelProxyManager, Error)
DECLARE_TYPE(tunnelProxyError, "TunnelProxyError", TunnelProxyServer, TunnelProxyError)
// Declare objects

View File

@ -31,7 +31,7 @@
#include "jsontypes.h"
#include "loggingcategories.h"
#include "tunnelproxy/tunnelproxymanager.h"
#include "tunnelproxy/tunnelproxyserver.h"
namespace remoteproxy {
@ -42,12 +42,38 @@ TunnelProxyHandler::TunnelProxyHandler(QObject *parent) : JsonHandler(parent)
params.clear(); returns.clear();
setDescription("RegisterServer", "Register a new TunnelProxy server on this instance. Multiple TunnelProxy clients can be connected to the registered server on success.");
params.insert("uuid", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("serverName", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("serverUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid));
setParams("RegisterServer", params);
returns.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorRef());
setReturns("RegisterServer", returns);
params.clear(); returns.clear();
setDescription("RegisterClient", "Register a new TunnelProxy client on TunnelProxy server with the given serverUuid..");
params.insert("clientName", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("clientUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid));
params.insert("serverUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid));
setParams("RegisterClient", params);
returns.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorRef());
setReturns("RegisterClient", returns);
// Notifications
params.clear(); returns.clear();
setDescription("ClientConnected", "Emitted whenever a new client has been connected to a registered server. "
"Only tunnel proxy clients registered as server will receive this notification.");
params.insert("clientId", JsonTypes::basicTypeToString(JsonTypes::UInt));
params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("address", JsonTypes::basicTypeToString(JsonTypes::String));
setParams("ClientConnected", params);
params.clear(); returns.clear();
setDescription("ClientDisconnected", "Emitted whenever a new client has been connected to a registered server. "
"Only tunnel proxy clients registered as server will receive this notification.");
params.insert("clientId", JsonTypes::basicTypeToString(JsonTypes::UInt));
params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("address", JsonTypes::basicTypeToString(JsonTypes::String));
setParams("ClientDisconnected", params);
}
QString TunnelProxyHandler::name() const
@ -58,14 +84,46 @@ QString TunnelProxyHandler::name() const
JsonReply *TunnelProxyHandler::RegisterServer(const QVariantMap &params, TransportClient *transportClient)
{
qCDebug(dcJsonRpc()) << name() << "register server" << params << transportClient;
QUuid serverUuid = params.value("uuid").toUuid();
QString serverName = params.value("name").toString();
TunnelProxyManager::Error error = Engine::instance()->tunnelProxyManager()->registerServer(transportClient->clientId(), serverUuid, serverName);
QUuid serverUuid = params.value("serverUuid").toUuid();
TunnelProxyServer::TunnelProxyError error = TunnelProxyServer::TunnelProxyErrorNoError;
if (serverUuid.isNull()) {
qCWarning(dcJsonRpc()) << "Invalid uuid received" << params.value("serverUuid").toString() << serverUuid;
error = TunnelProxyServer::TunnelProxyErrorInvalidUuid;
} else {
QString serverName = params.value("serverName").toString();
error = Engine::instance()->tunnelProxyServer()->registerServer(transportClient->clientId(), serverUuid, serverName);
}
QVariantMap response;
response.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorToString(error));
return createReply("RegisterServer", response);
}
JsonReply *TunnelProxyHandler::RegisterClient(const QVariantMap &params, TransportClient *transportClient)
{
qCDebug(dcJsonRpc()) << name() << "register client" << params << transportClient;
QString clientName = params.value("clientName").toString();
QUuid clientUuid = params.value("clientUuid").toUuid();
QUuid serverUuid = params.value("serverUuid").toUuid();
TunnelProxyServer::TunnelProxyError error = TunnelProxyServer::TunnelProxyErrorNoError;
if (serverUuid.isNull()) {
qCWarning(dcJsonRpc()) << "Invalid server uuid received" << params.value("serverUuid").toString() << serverUuid;
error = TunnelProxyServer::TunnelProxyErrorInvalidUuid;
} else if (clientUuid.isNull()) {
qCWarning(dcJsonRpc()) << "Invalid client uuid received" << params.value("clientUuid").toString() << clientUuid;
error = TunnelProxyServer::TunnelProxyErrorInvalidUuid;
} else {
error = Engine::instance()->tunnelProxyServer()->registerClient(transportClient->clientId(), clientUuid, clientName, serverUuid);
}
QVariantMap response;
response.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorToString(error));
return createReply("RegisterServer", response);
}
//JsonReply *TunnelProxyHandler::RemoveClient(const QVariantMap &params, TransportClient *transportClient)
//{
//}
}

View File

@ -46,9 +46,13 @@ public:
QString name() const override;
Q_INVOKABLE JsonReply *RegisterServer(const QVariantMap &params, TransportClient *transportClient);
Q_INVOKABLE JsonReply *RegisterClient(const QVariantMap &params, TransportClient *transportClient);
// Q_INVOKABLE JsonReply *RemoveClient(const QVariantMap &params, TransportClient *transportClient);
signals:
void ClientConnected(const QVariantMap &params, TransportClient *transportClient);
void ClientDisconnected(const QVariantMap &params, TransportClient *transportClient);
};

View File

@ -30,8 +30,9 @@ HEADERS += \
server/jsonrpcserver.h \
server/transportclient.h \
server/monitorserver.h \
tunnelproxy/tunnelproxyclient.h \
tunnelproxy/tunnelproxyclientconnection.h \
tunnelproxy/tunnelproxymanager.h \
tunnelproxy/tunnelproxyserver.h \
tunnelproxy/tunnelproxyserverconnection.h
SOURCES += \
@ -61,8 +62,9 @@ SOURCES += \
server/websocketserver.cpp \
server/jsonrpcserver.cpp \
server/monitorserver.cpp \
tunnelproxy/tunnelproxyclient.cpp \
tunnelproxy/tunnelproxyclientconnection.cpp \
tunnelproxy/tunnelproxymanager.cpp \
tunnelproxy/tunnelproxyserver.cpp \
tunnelproxy/tunnelproxyserverconnection.cpp

View File

@ -39,9 +39,8 @@ Q_LOGGING_CATEGORY(dcWebSocketServerTraffic, "WebSocketServerTraffic")
Q_LOGGING_CATEGORY(dcAuthentication, "Authentication")
Q_LOGGING_CATEGORY(dcAuthenticationProcess, "AuthenticationProcess")
Q_LOGGING_CATEGORY(dcProxyServer, "ProxyServer")
Q_LOGGING_CATEGORY(dcTunnelProxyManager, "TunnelProxyManager")
Q_LOGGING_CATEGORY(dcProxyTunnelClient, "ProxyTunnelClient")
Q_LOGGING_CATEGORY(dcTunnelProxyServer, "TunnelProxyServer")
Q_LOGGING_CATEGORY(dcTunnelProxyServerTraffic, "TunnelProxyServerTraffic")
Q_LOGGING_CATEGORY(dcProxyServerTraffic, "ProxyServerTraffic")
Q_LOGGING_CATEGORY(dcMonitorServer, "MonitorServer")
Q_LOGGING_CATEGORY(dcAwsCredentialsProvider, "AwsCredentialsProvider")

View File

@ -43,10 +43,9 @@ Q_DECLARE_LOGGING_CATEGORY(dcTcpSocketServerTraffic)
Q_DECLARE_LOGGING_CATEGORY(dcAuthentication)
Q_DECLARE_LOGGING_CATEGORY(dcAuthenticationProcess)
Q_DECLARE_LOGGING_CATEGORY(dcProxyServer)
Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxyManager)
Q_DECLARE_LOGGING_CATEGORY(dcProxyTunnelClient)
Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxyServer)
Q_DECLARE_LOGGING_CATEGORY(dcProxyServerTraffic)
Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxyServer)
Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxyServerTraffic)
Q_DECLARE_LOGGING_CATEGORY(dcMonitorServer)
Q_DECLARE_LOGGING_CATEGORY(dcAwsCredentialsProvider)
Q_DECLARE_LOGGING_CATEGORY(dcAwsCredentialsProviderTraffic)

View File

@ -81,26 +81,6 @@ void ProxyClient::setUserName(const QString &userName)
m_userName = userName;
}
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::tunnelIdentifier() const
{
return m_token + m_nonce;

View File

@ -60,13 +60,6 @@ public:
QString userName() const;
void setUserName(const QString &userName);
// Properties from auth request
QString uuid() const;
void setUuid(const QString &uuid);
QString name() const;
void setName(const QString &name);
QString tunnelIdentifier() const;
QString token() const;
@ -89,8 +82,6 @@ private:
bool m_authenticated = false;
bool m_tunnelConnected = false;
QString m_uuid;
QString m_name;
QString m_token;
QString m_nonce;

View File

@ -306,6 +306,7 @@ void JsonRpcServer::unregisterClient(TransportClient *transportClient)
qCWarning(dcJsonRpc()) << "Client was not registered" << transportClient;
return;
}
m_clients.removeAll(transportClient);
if (m_asyncReplies.values().contains(transportClient)) {

View File

@ -66,6 +66,26 @@ TransportInterface *TransportClient::interface() const
return m_interface;
}
QUuid TransportClient::uuid() const
{
return m_uuid;
}
void TransportClient::setUuid(const QUuid &uuid)
{
m_uuid = uuid;
}
QString TransportClient::name() const
{
return m_name;
}
void TransportClient::setName(const QString &name)
{
m_name = name;
}
quint64 TransportClient::rxDataCount() const
{
return m_rxDataCount;

View File

@ -52,6 +52,13 @@ public:
TransportInterface *interface() const;
// Properties from auth request
QUuid uuid() const;
void setUuid(const QUuid &uuid);
QString name() const;
void setName(const QString &name);
quint64 rxDataCount() const;
void addRxDataCount(int dataCount);
@ -74,6 +81,10 @@ protected:
QHostAddress m_peerAddress;
uint m_creationTimeStamp = 0;
// Eveyone has to register him self everywhere with a name and a uuid
QString m_name;
QUuid m_uuid;
QByteArray m_dataBuffers;
// Json data information

View File

@ -0,0 +1,62 @@
#include "tunnelproxyclient.h"
#include "server/transportinterface.h"
namespace remoteproxy {
TunnelProxyClient::TunnelProxyClient(TransportInterface *interface, const QUuid &clientId, const QHostAddress &address, QObject *parent) :
TransportClient(interface, clientId, address, parent)
{
}
TunnelProxyClient::Type TunnelProxyClient::type() const
{
return m_type;
}
void TunnelProxyClient::setType(Type type)
{
if (m_type == type)
return;
m_type = type;
emit typeChanged(m_type);
}
QList<QByteArray> TunnelProxyClient::processData(const QByteArray &data)
{
// TODO: unescape if this data is for the json handler
QList<QByteArray> packages;
// Handle packet fragmentation
m_dataBuffers.append(data);
int splitIndex = m_dataBuffers.indexOf("}\n{");
while (splitIndex > -1) {
packages.append(m_dataBuffers.left(splitIndex + 1));
m_dataBuffers = m_dataBuffers.right(m_dataBuffers.length() - splitIndex - 2);
splitIndex = m_dataBuffers.indexOf("}\n{");
}
if (m_dataBuffers.trimmed().endsWith("}")) {
packages.append(m_dataBuffers);
m_dataBuffers.clear();
}
return packages;
}
QDebug operator<<(QDebug debug, TunnelProxyClient *tunnelProxyClient)
{
debug.nospace() << "TunnelProxyClient(";
if (!tunnelProxyClient->name().isEmpty()) {
debug.nospace() << tunnelProxyClient->name() << ", ";
}
debug.nospace() << tunnelProxyClient->interface()->serverName();
debug.nospace() << ", " << tunnelProxyClient->clientId().toString();
debug.nospace() << ", " << tunnelProxyClient->peerAddress().toString();
debug.nospace() << ", " << tunnelProxyClient->creationTimeString() << ")";
return debug.space();
}
}

View File

@ -0,0 +1,41 @@
#ifndef TUNNELPROXYCLIENT_H
#define TUNNELPROXYCLIENT_H
#include <QObject>
#include "server/transportclient.h"
namespace remoteproxy {
class TunnelProxyClient : public TransportClient
{
Q_OBJECT
public:
enum Type {
TypeNone,
TypeServer,
TypeClient
};
Q_ENUM(Type)
explicit TunnelProxyClient(TransportInterface *interface, const QUuid &clientId, const QHostAddress &address, QObject *parent = nullptr);
Type type() const;
void setType(Type type);
// Json server methods
QList<QByteArray> processData(const QByteArray &data) override;
signals:
void typeChanged(Type type);
private:
Type m_type = TypeNone;
};
QDebug operator<< (QDebug debug, TunnelProxyClient *tunnelProxyClient);
}
#endif // TUNNELPROXYCLIENT_H

View File

@ -26,12 +26,38 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "tunnelproxyclientconnection.h"
#include "server/transportclient.h"
namespace remoteproxy {
TunnelProxyClientConnection::TunnelProxyClientConnection(QObject *parent) : QObject(parent)
TunnelProxyClientConnection::TunnelProxyClientConnection(TransportClient *transportClient, const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid, QObject *parent) :
QObject(parent),
m_transportClient(transportClient),
m_clientUuid(clientUuid),
m_clientName(clientName),
m_serverUuid(serverUuid)
{
}
TransportClient *TunnelProxyClientConnection::transportClient() const
{
return m_transportClient;
}
QUuid TunnelProxyClientConnection::clientUuid() const
{
return m_clientUuid;
}
QString TunnelProxyClientConnection::clientName() const
{
return m_clientName;
}
QUuid TunnelProxyClientConnection::serverUuid() const
{
return m_serverUuid;
}
}

View File

@ -28,18 +28,34 @@
#ifndef TUNNELPROXYCLIENTCONNECTION_H
#define TUNNELPROXYCLIENTCONNECTION_H
#include <QUuid>
#include <QObject>
namespace remoteproxy {
class TransportClient;
class TunnelProxyClientConnection : public QObject
{
Q_OBJECT
public:
explicit TunnelProxyClientConnection(QObject *parent = nullptr);
explicit TunnelProxyClientConnection(TransportClient *transportClient, const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid, QObject *parent = nullptr);
TransportClient *transportClient() const;
QUuid clientUuid() const;
QString clientName() const;
QUuid serverUuid() const;
signals:
private:
TransportClient *m_transportClient = nullptr;
QUuid m_clientUuid;
QString m_clientName;
QUuid m_serverUuid;
};
}

View File

@ -1,163 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU General Public License Usage
* Alternatively, this project may be redistributed and/or modified under
* the terms of the GNU General Public License as published by the Free Software Foundation,
* GNU version 3. this project is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "tunnelproxymanager.h"
#include "loggingcategories.h"
#include "jsonrpc/tunnelproxyhandler.h"
namespace remoteproxy {
TunnelProxyManager::TunnelProxyManager(QObject *parent) :
QObject(parent)
{
m_jsonRpcServer = new JsonRpcServer(this);
m_jsonRpcServer->registerHandler(m_jsonRpcServer);
m_jsonRpcServer->registerHandler(new TunnelProxyHandler(this));
}
TunnelProxyManager::~TunnelProxyManager()
{
}
bool TunnelProxyManager::running() const
{
return m_running;
}
void TunnelProxyManager::setRunning(bool running)
{
if (m_running == running)
return;
qCDebug(dcTunnelProxyManager()) << "The proxy tunnel manager is" << (running ? "now up and running." : "not running any more.");
m_running = running;
emit runningChanged(m_running);
}
void TunnelProxyManager::registerTransportInterface(TransportInterface *interface)
{
qCDebug(dcTunnelProxyManager()) << "Register transport interface" << interface->serverName();
if (m_transportInterfaces.contains(interface)) {
qCWarning(dcTunnelProxyManager()) << "Transport interface already registerd.";
return;
}
connect(interface, &TransportInterface::clientConnected, this, &TunnelProxyManager::onClientConnected);
connect(interface, &TransportInterface::clientDisconnected, this, &TunnelProxyManager::onClientDisconnected);
connect(interface, &TransportInterface::dataAvailable, this, &TunnelProxyManager::onClientDataAvailable);
m_transportInterfaces.append(interface);
}
TunnelProxyManager::Error TunnelProxyManager::registerServer(const QUuid &clientId, const QUuid &serverUuid, const QString &serverName)
{
qCDebug(dcTunnelProxyManager()) << "Register new server" << m_proxyClients.value(clientId) << serverName << serverUuid.toString();
// TODO: check if uuid already exists
// Check if requested already as client
TunnelProxyServerConnection *proxyServer = new TunnelProxyServerConnection(m_proxyClients.value(clientId), serverUuid, serverName);
m_proxyClientsTunnelServer.insert(clientId, proxyServer);
m_tunnelServers.insert(proxyServer->serverUuid(), proxyServer);
return ErrorNoError;
}
void TunnelProxyManager::startServer()
{
qCDebug(dcTunnelProxyManager()) << "Starting tunnel proxy...";
foreach (TransportInterface *interface, m_transportInterfaces) {
interface->startServer();
}
setRunning(true);
}
void TunnelProxyManager::stopServer()
{
qCDebug(dcTunnelProxyManager()) << "Stopping tunnel proxy...";
foreach (TransportInterface *interface, m_transportInterfaces) {
interface->stopServer();
}
setRunning(false);
}
void TunnelProxyManager::tick()
{
}
void TunnelProxyManager::onClientConnected(const QUuid &clientId, const QHostAddress &address)
{
TransportInterface *interface = static_cast<TransportInterface *>(sender());
qCDebug(dcTunnelProxyManager()) << "New client connected" << interface->serverName() << clientId.toString() << address.toString();
ProxyClient *proxyClient = new ProxyClient(interface, clientId, address, this);
m_proxyClients.insert(clientId, proxyClient);
m_jsonRpcServer->registerClient(proxyClient);
}
void TunnelProxyManager::onClientDisconnected(const QUuid &clientId)
{
TransportInterface *interface = static_cast<TransportInterface *>(sender());
qCDebug(dcProxyServer()) << "Client disconnected" << interface->serverName() << clientId.toString();
if (!m_proxyClients.contains(clientId)) {
qCWarning(dcProxyServer()) << "Unknown client disconnected from proxy server." << clientId.toString();
return;
}
ProxyClient *proxyClient = m_proxyClients.take(clientId);
// Unregister from json rpc server
m_jsonRpcServer->unregisterClient(proxyClient);
// Delete the proxy client
proxyClient->deleteLater();
}
void TunnelProxyManager::onClientDataAvailable(const QUuid &clientId, const QByteArray &data)
{
Q_UNUSED(clientId)
Q_UNUSED(data)
ProxyClient *proxyClient = m_proxyClients.value(clientId);
if (!proxyClient) {
qCWarning(dcProxyServer()) << "Could not find client for uuid" << clientId;
return;
}
qCDebug(dcProxyServerTraffic()) << "Client data available" << proxyClient << qUtf8Printable(data);
m_jsonRpcServer->processData(proxyClient, data);
}
}

View File

@ -0,0 +1,238 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU General Public License Usage
* Alternatively, this project may be redistributed and/or modified under
* the terms of the GNU General Public License as published by the Free Software Foundation,
* GNU version 3. this project is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "tunnelproxyserver.h"
#include "loggingcategories.h"
#include "jsonrpc/tunnelproxyhandler.h"
#include "tunnelproxyserverconnection.h"
#include "tunnelproxyclientconnection.h"
namespace remoteproxy {
TunnelProxyServer::TunnelProxyServer(QObject *parent) :
QObject(parent)
{
m_jsonRpcServer = new JsonRpcServer(this);
m_jsonRpcServer->registerHandler(m_jsonRpcServer);
m_jsonRpcServer->registerHandler(new TunnelProxyHandler(this));
}
TunnelProxyServer::~TunnelProxyServer()
{
}
bool TunnelProxyServer::running() const
{
return m_running;
}
void TunnelProxyServer::setRunning(bool running)
{
if (m_running == running)
return;
qCDebug(dcTunnelProxyServer()) << "The proxy tunnel manager is" << (running ? "now up and running." : "not running any more.");
m_running = running;
emit runningChanged(m_running);
}
void TunnelProxyServer::registerTransportInterface(TransportInterface *interface)
{
qCDebug(dcTunnelProxyServer()) << "Register transport interface" << interface->serverName();
if (m_transportInterfaces.contains(interface)) {
qCWarning(dcTunnelProxyServer()) << "Transport interface already registerd.";
return;
}
connect(interface, &TransportInterface::clientConnected, this, &TunnelProxyServer::onClientConnected);
connect(interface, &TransportInterface::clientDisconnected, this, &TunnelProxyServer::onClientDisconnected);
connect(interface, &TransportInterface::dataAvailable, this, &TunnelProxyServer::onClientDataAvailable);
m_transportInterfaces.append(interface);
}
TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerServer(const QUuid &clientId, const QUuid &serverUuid, const QString &serverName)
{
qCDebug(dcTunnelProxyServer()) << "Register new server" << m_proxyClients.value(clientId) << serverName << serverUuid.toString();
// Check if requested already as client
TunnelProxyClient *tunnelProxyClient = m_proxyClients.value(clientId);
if (!tunnelProxyClient) {
qCWarning(dcTunnelProxyServer()) << "There is no client with client uuid" << clientId.toString();
return TunnelProxyServer::TunnelProxyErrorInternalServerError;
}
// Make sure this client has not been registered as client or re-registration has been called...
if (tunnelProxyClient->type() != TunnelProxyClient::TypeNone) {
qCWarning(dcTunnelProxyServer()) << "Client tried to register as server but has already been registerd as" << tunnelProxyClient->type();
return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
}
tunnelProxyClient->setType(TunnelProxyClient::TypeServer);
tunnelProxyClient->setUuid(serverUuid);
tunnelProxyClient->setName(serverName);
TunnelProxyServerConnection *serverConnection = new TunnelProxyServerConnection(tunnelProxyClient, serverUuid, serverName, this);
m_tunnelProxyServerConnections.insert(serverUuid, serverConnection);
return TunnelProxyServer::TunnelProxyErrorNoError;
}
TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerClient(const QUuid &clientId, const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid)
{
qCDebug(dcTunnelProxyServer()) << "Register new client" << m_proxyClients.value(clientId) << clientName << clientUuid.toString() << "--> server" << serverUuid.toString();
TunnelProxyClient *tunnelProxyClient = m_proxyClients.value(clientId);
if (!tunnelProxyClient) {
qCWarning(dcTunnelProxyServer()) << "There is no client with client uuid" << clientId.toString();
return TunnelProxyServer::TunnelProxyErrorInternalServerError;
}
// Make sure this client has not been registered as client or re-registration has been called...
if (tunnelProxyClient->type() != TunnelProxyClient::TypeNone) {
qCWarning(dcTunnelProxyServer()) << "Client tried to register as client but has already been registerd as" << tunnelProxyClient->type();
return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
}
// Get the desired server connection
TunnelProxyServerConnection *serverConnection = m_tunnelProxyServerConnections.value(serverUuid);
if (!serverConnection) {
qCWarning(dcTunnelProxyServer()) << "There is no server registered with server uuid" << serverUuid.toString();
return TunnelProxyServer::TunnelProxyErrorServerNotFound;
}
if (m_tunnelProxyClientConnections.contains(clientUuid)) {
qCWarning(dcTunnelProxyServer()) << "There is a client already registered with client uuid" << clientUuid.toString();
return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
}
// Not registered yet, we have a connected server for the requested server uuid
tunnelProxyClient->setType(TunnelProxyClient::TypeClient);
tunnelProxyClient->setUuid(clientUuid);
tunnelProxyClient->setName(clientName);
TunnelProxyClientConnection *clientConnection = new TunnelProxyClientConnection(tunnelProxyClient, clientUuid, clientName, serverUuid);
m_tunnelProxyClientConnections.insert(clientUuid, clientConnection);
// TODO: register on the server and wait for te aprovement from the server
return TunnelProxyServer::TunnelProxyErrorNoError;
}
void TunnelProxyServer::startServer()
{
qCDebug(dcTunnelProxyServer()) << "Starting tunnel proxy...";
foreach (TransportInterface *interface, m_transportInterfaces) {
interface->startServer();
}
setRunning(true);
}
void TunnelProxyServer::stopServer()
{
qCDebug(dcTunnelProxyServer()) << "Stopping tunnel proxy...";
foreach (TransportInterface *interface, m_transportInterfaces) {
interface->stopServer();
}
setRunning(false);
}
void TunnelProxyServer::tick()
{
}
void TunnelProxyServer::onClientConnected(const QUuid &clientId, const QHostAddress &address)
{
TransportInterface *interface = static_cast<TransportInterface *>(sender());
qCDebug(dcTunnelProxyServer()) << "New client connected" << interface->serverName() << clientId.toString() << address.toString();
TunnelProxyClient *tunnelProxyClient = new TunnelProxyClient(interface, clientId, address, this);
m_proxyClients.insert(clientId, tunnelProxyClient);
m_jsonRpcServer->registerClient(tunnelProxyClient);
}
void TunnelProxyServer::onClientDisconnected(const QUuid &clientId)
{
TransportInterface *interface = static_cast<TransportInterface *>(sender());
qCDebug(dcTunnelProxyServer()) << "Client disconnected" << interface->serverName() << clientId.toString();
TunnelProxyClient *tunnelProxyClient = m_proxyClients.take(clientId);
if (!tunnelProxyClient) {
qCWarning(dcTunnelProxyServer()) << "Unknown client disconnected from proxy server." << clientId.toString();
return;
}
if (tunnelProxyClient->type() == TunnelProxyClient::TypeServer) {
TunnelProxyServerConnection *serverConnection = m_tunnelProxyServerConnections.take(tunnelProxyClient->uuid());
if (!serverConnection) {
qCWarning(dcTunnelProxyServer()) << "Could not find server connection for disconnected tunnel proxy client claiming to be a server.";
} else {
// TODO: kill all related clients
serverConnection->deleteLater();
}
}
if (tunnelProxyClient->type() == TunnelProxyClient::TypeClient) {
TunnelProxyClientConnection *clientConnection = m_tunnelProxyClientConnections.take(tunnelProxyClient->uuid());
if (!clientConnection) {
qCWarning(dcTunnelProxyServer()) << "Could not find client connection for disconnected tunnel proxy client claiming to be a client.";
} else {
// TODO: remove from server
clientConnection->deleteLater();
}
}
// Unregister from json rpc server
m_jsonRpcServer->unregisterClient(tunnelProxyClient);
// Delete the proxy client
tunnelProxyClient->deleteLater();
}
void TunnelProxyServer::onClientDataAvailable(const QUuid &clientId, const QByteArray &data)
{
TunnelProxyClient *tunnelProxyClient = m_proxyClients.value(clientId);
if (!tunnelProxyClient) {
qCWarning(dcTunnelProxyServer()) << "Data received but could not find client for uuid" << clientId;
return;
}
qCDebug(dcTunnelProxyServerTraffic()) << "Client data available" << tunnelProxyClient << qUtf8Printable(data);
// TODO: verify if encoded and for whom this data is... 0x0000 is for the json rpc handler...
m_jsonRpcServer->processData(tunnelProxyClient, data);
}
}

View File

@ -25,37 +25,43 @@
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef TUNNELPROXYMANAGER_H
#define TUNNELPROXYMANAGER_H
#ifndef TUNNELPROXYSERVER_H
#define TUNNELPROXYSERVER_H
#include <QObject>
#include "server/jsonrpcserver.h"
#include "server/transportinterface.h"
#include "tunnelproxyserverconnection.h"
#include "tunnelproxyclient.h"
namespace remoteproxy {
class TunnelProxyManager : public QObject
class TunnelProxyServerConnection;
class TunnelProxyClientConnection;
class TunnelProxyServer : public QObject
{
Q_OBJECT
public:
enum Error {
ErrorNoError,
ErrorServerNotFound
enum TunnelProxyError {
TunnelProxyErrorNoError,
TunnelProxyErrorInvalidUuid,
TunnelProxyErrorInternalServerError,
TunnelProxyErrorServerNotFound,
TunnelProxyErrorAlreadyRegistered
};
Q_ENUM(Error)
Q_ENUM(TunnelProxyError)
explicit TunnelProxyManager(QObject *parent = nullptr);
~TunnelProxyManager();
explicit TunnelProxyServer(QObject *parent = nullptr);
~TunnelProxyServer();
bool running() const;
void setRunning(bool running);
void registerTransportInterface(TransportInterface *interface);
TunnelProxyManager::Error registerServer(const QUuid &clientId, const QUuid &serverUuid, const QString &serverName);
TunnelProxyServer::TunnelProxyError registerServer(const QUuid &clientId, const QUuid &serverUuid, const QString &serverName);
TunnelProxyServer::TunnelProxyError registerClient(const QUuid &clientId, const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid);
public slots:
void startServer();
@ -71,23 +77,20 @@ private slots:
void onClientDisconnected(const QUuid &clientId);
void onClientDataAvailable(const QUuid &clientId, const QByteArray &data);
// void onTunnelProxyServerRegistered();
// void onProxyTunnelClientRegistered();
private:
JsonRpcServer *m_jsonRpcServer = nullptr;
QList<TransportInterface *> m_transportInterfaces;
bool m_running = false;
QHash<QUuid, ProxyClient *> m_proxyClients; // clients
QHash<QUuid, TunnelProxyClient *> m_proxyClients; // clientId, object
// Server connections
QHash<QUuid, TunnelProxyServerConnection *> m_proxyClientsTunnelServer; // clientUuid, object
QHash<QUuid, TunnelProxyServerConnection *> m_tunnelServers; // server uuid, object
QHash<QUuid, TunnelProxyServerConnection *> m_tunnelProxyServerConnections; // server uuid, object
QHash<QUuid, TunnelProxyClientConnection *> m_tunnelProxyClientConnections; // client uuid, object
};
}
#endif // TUNNELPROXYMANAGER_H
#endif // TUNNELPROXYSERVER_H

View File

@ -26,21 +26,22 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "tunnelproxyserverconnection.h"
#include "server/transportclient.h"
namespace remoteproxy {
TunnelProxyServerConnection::TunnelProxyServerConnection(ProxyClient *proxyClient, const QUuid &serverUuid, const QString &serverName, QObject *parent) :
TunnelProxyServerConnection::TunnelProxyServerConnection(TransportClient *transportClient, const QUuid &serverUuid, const QString &serverName, QObject *parent) :
QObject(parent),
m_proxyClient(proxyClient),
m_transportClient(transportClient),
m_serverUuid(serverUuid),
m_serverName(serverName)
{
}
ProxyClient *TunnelProxyServerConnection::proxyClient() const
TransportClient *TunnelProxyServerConnection::transportClient() const
{
return m_proxyClient;
return m_transportClient;
}
QUuid TunnelProxyServerConnection::serverUuid() const

View File

@ -28,27 +28,30 @@
#ifndef TUNNELPROXYSERVERCONNECTION_H
#define TUNNELPROXYSERVERCONNECTION_H
#include <QUuid>
#include <QObject>
#include "proxy/proxyclient.h"
namespace remoteproxy {
class TransportClient;
class TunnelProxyServerConnection : public QObject
{
Q_OBJECT
public:
explicit TunnelProxyServerConnection(ProxyClient *proxyClient, const QUuid &serverUuid, const QString &serverName, QObject *parent = nullptr);
explicit TunnelProxyServerConnection(TransportClient *transportClient, const QUuid &serverUuid, const QString &serverName, QObject *parent = nullptr);
ProxyClient *proxyClient() const;
TransportClient *transportClient() const;
QUuid serverUuid() const;
QString serverName() const;
signals:
private:
ProxyClient *m_proxyClient = nullptr;
TransportClient *m_transportClient = nullptr;
QUuid m_serverUuid;
QString m_serverName;

View File

@ -199,4 +199,140 @@ void RemoteProxyTestsTunnelProxy::apiBasicCalls()
stopServer();
}
void RemoteProxyTestsTunnelProxy::registerServer_data()
{
QTest::addColumn<QString>("name");
QTest::addColumn<QString>("uuid");
QTest::addColumn<TunnelProxyServer::TunnelProxyError>("expectedError");
QTest::newRow("valid call") << "valid server" << QUuid::createUuid().toString() << TunnelProxyServer::TunnelProxyErrorNoError;
QTest::newRow("valid uuid with brackets") << "valid server" << "{00323a95-d1ab-4752-88d4-dbc8b9015b0f}" << TunnelProxyServer::TunnelProxyErrorNoError;
QTest::newRow("valid uuid without brackets") << "valid server" << "00323a95-d1ab-4752-88d4-dbc8b9015b0f" << TunnelProxyServer::TunnelProxyErrorNoError;
QTest::newRow("invalid null uuid") << "valid server" << QUuid().toString() << TunnelProxyServer::TunnelProxyErrorInvalidUuid;
QTest::newRow("invalid null uuid") << "valid server" << "foo" << TunnelProxyServer::TunnelProxyErrorInvalidUuid;
}
void RemoteProxyTestsTunnelProxy::registerServer()
{
QFETCH(QString, name);
QFETCH(QString, uuid);
QFETCH(TunnelProxyServer::TunnelProxyError, expectedError);
// Start the server
startServer();
resetDebugCategories();
addDebugCategory("TunnelProxyServer.debug=true");
// Register a new server
QVariantMap params;
params.insert("serverName", name);
params.insert("serverUuid", uuid);
// Websocket
QVariantMap response = invokeWebSocketTunnelProxyApiCall("TunnelProxy.RegisterServer", params).toMap();
QVERIFY(!response.isEmpty());
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response, expectedError);
// TCP Socket
response = invokeTcpSocketTunnelProxyApiCall("TunnelProxy.RegisterServer", params).toMap();
QVERIFY(!response.isEmpty());
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response, expectedError);
// Clean up
stopServer();
}
void RemoteProxyTestsTunnelProxy::registerServerDuplicated()
{
// Start the server
startServer();
resetDebugCategories();
addDebugCategory("TunnelProxyServer.debug=true");
// Register a new server
QString serverName = "tunnel proxy server awesome nymea installation";
QUuid serverUuid = QUuid::createUuid();
QVariantMap params;
params.insert("serverName", serverName);
params.insert("serverUuid", serverUuid.toString());
// TCP socket
QPair<QVariant, QSslSocket *> result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params);
QVariantMap response = result.first.toMap();
QSslSocket *socket = result.second;
QVERIFY(!response.isEmpty());
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response);
// Try to register again with the same uuid on the same socket
result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, socket);
response = result.first.toMap();
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered);
// Try to register an invalid server uuid
QVariantMap paramsInvalidUuid;
paramsInvalidUuid.insert("serverName", serverName);
paramsInvalidUuid.insert("serverUuid", QUuid().toString());
result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", paramsInvalidUuid, socket);
response = result.first.toMap();
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorInvalidUuid);
// Close the tcp socket
socket->close();
delete socket;
QTest::qWait(100);
// WebSocket
// Try to register from a websocket with the same uuid
QPair<QVariant, QWebSocket *> resultWebSocket = invokeWebSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params);
response = resultWebSocket.first.toMap();
QWebSocket *webSocket = resultWebSocket.second;
QVERIFY(!response.isEmpty());
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response);
// Try to register again with the same uuid on the same socket
resultWebSocket = invokeWebSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, webSocket);
response = resultWebSocket.first.toMap();
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered);
// Try to register an invalid server uuid
resultWebSocket = invokeWebSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", paramsInvalidUuid, webSocket);
response = resultWebSocket.first.toMap();
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorInvalidUuid);
webSocket->close();
delete webSocket;
QTest::qWait(100);
resetDebugCategories();
// Clean up
stopServer();
}
QTEST_MAIN(RemoteProxyTestsTunnelProxy)

View File

@ -54,6 +54,12 @@ private slots:
void apiBasicCallsTcp_data();
void apiBasicCallsTcp();
void registerServer_data();
void registerServer();
void registerServerDuplicated();
};
#endif // REMOTEPROXYTESTSTUNNELPROXY_H

View File

@ -42,7 +42,7 @@
BaseTest::BaseTest(QObject *parent) :
QObject(parent)
{
resetDebugCategories();
}
void BaseTest::loadConfiguration(const QString &fileName)
@ -52,6 +52,19 @@ void BaseTest::loadConfiguration(const QString &fileName)
//restartEngine();
}
void BaseTest::resetDebugCategories()
{
m_currentDebugCategories = m_defaultDebugCategories;
QLoggingCategory::setFilterRules(m_currentDebugCategories);
}
void BaseTest::addDebugCategory(const QString &debugCategory)
{
m_currentDebugCategories += debugCategory + "\n";
//qDebug() << m_currentDebugCategories;
QLoggingCategory::setFilterRules(m_currentDebugCategories);
}
void BaseTest::cleanUpEngine()
{
qDebug() << "Clean up engine";
@ -87,8 +100,6 @@ void BaseTest::restartEngine()
void BaseTest::startEngine()
{
QLoggingCategory::setFilterRules("*.debug=false\ndefault.debug=true\nApplication.debug=true");
m_configuration = new ProxyConfiguration(this);
loadConfiguration(":/test-configuration.conf");
@ -302,8 +313,8 @@ bool BaseTest::createRemoteConnection(const QString &token, const QString &nonce
QString nameConnectionTwo = "Test client two";
QUuid uuidConnectionTwo = QUuid::createUuid();
// QByteArray dataOne = "Hello from client one :-)";
// QByteArray dataTwo = "Hello from client two :-)";
// QByteArray dataOne = "Hello from client one :-)";
// QByteArray dataTwo = "Hello from client two :-)";
// Create two connection
RemoteProxyConnection *connectionOne = new RemoteProxyConnection(uuidConnectionOne, nameConnectionOne, parent);
@ -466,6 +477,7 @@ QVariant BaseTest::invokeWebSocketTunnelProxyApiCall(const QString &method, cons
socket->open(Engine::instance()->webSocketServerTunnelProxy()->serverUrl());
spyConnection.wait();
if (spyConnection.count() == 0) {
qWarning() << "Failed to connect websocket on tunnel proxy";
return QVariant();
}
@ -473,8 +485,10 @@ QVariant BaseTest::invokeWebSocketTunnelProxyApiCall(const QString &method, cons
socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact)));
dataSpy.wait();
socket->close();
socket->deleteLater();
if (!remainsConnected) {
socket->close();
socket->deleteLater();
}
for (int i = 0; i < dataSpy.count(); i++) {
// Make sure the response ends with '}\n'
@ -651,6 +665,125 @@ QVariant BaseTest::injectTcpSocketTunnelProxyData(const QByteArray &data)
return jsonDoc.toVariant();
}
QPair<QVariant, QSslSocket *> BaseTest::invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params, QSslSocket *existingSocket)
{
QSslSocket *socket = nullptr;
if (!existingSocket) {
socket = new QSslSocket(this);
typedef void (QSslSocket:: *sslErrorsSignal)(const QList<QSslError> &);
QObject::connect(socket, static_cast<sslErrorsSignal>(&QSslSocket::sslErrors), this, &BaseTest::sslSocketSslErrors);
QSignalSpy spyConnection(socket, &QSslSocket::connected);
socket->connectToHostEncrypted(Engine::instance()->tcpSocketServerTunnelProxy()->serverUrl().host(),
static_cast<quint16>(Engine::instance()->tcpSocketServerTunnelProxy()->serverUrl().port()));
spyConnection.wait();
if (spyConnection.count() == 0) {
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
}
} else {
socket = existingSocket;
}
QVariantMap request;
request.insert("id", m_commandCounter);
request.insert("method", method);
request.insert("params", params);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(request);
QSignalSpy dataSpy(socket, &QSslSocket::readyRead);
socket->write(jsonDoc.toJson(QJsonDocument::Compact) + '\n');
// FIXME: check why it waits the full time here
dataSpy.wait(500);
if (dataSpy.count() != 1) {
qWarning() << "No data received";
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
}
QByteArray data = socket->readAll();
// Make sure the response ends with '}\n'
if (!data.endsWith("}\n")) {
qWarning() << "JSON data does not end with \"}\n\"";
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
}
// Make sure the response it a valid JSON string
QJsonParseError error;
jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
qWarning() << "JSON parser error" << error.errorString();
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
}
QVariantMap response = jsonDoc.toVariant().toMap();
if (response.value("id").toInt() == m_commandCounter) {
m_commandCounter++;
return QPair<QVariant, QSslSocket *>(jsonDoc.toVariant(), socket);
}
m_commandCounter++;
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
}
QPair<QVariant, QWebSocket *> BaseTest::invokeWebSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params, QWebSocket *existingSocket)
{
QWebSocket *socket = nullptr;
if (!existingSocket) {
socket = new QWebSocket("tunnelproxy-testclient", QWebSocketProtocol::Version13);
connect(socket, &QWebSocket::sslErrors, this, &BaseTest::sslErrors);
QSignalSpy spyConnection(socket, SIGNAL(connected()));
socket->open(Engine::instance()->webSocketServerTunnelProxy()->serverUrl());
spyConnection.wait();
if (spyConnection.count() == 0) {
qWarning() << "Failed to connect websocket on tunnel proxy";
return QPair<QVariant, QWebSocket *>(QVariant(), socket);
}
} else {
socket = existingSocket;
}
QVariantMap request;
request.insert("id", m_commandCounter);
request.insert("method", method);
request.insert("params", params);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(request);
QSignalSpy dataSpy(socket, SIGNAL(textMessageReceived(QString)));
socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact)));
dataSpy.wait();
for (int i = 0; i < dataSpy.count(); i++) {
// Make sure the response ends with '}\n'
if (!dataSpy.at(i).last().toByteArray().endsWith("}\n")) {
qWarning() << "JSON data does not end with \"}\n\"";
return QPair<QVariant, QWebSocket *>(QVariant(), socket);
}
// 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 QPair<QVariant, QWebSocket *>(QVariant(), socket);
}
QVariantMap response = jsonDoc.toVariant().toMap();
// skip notifications
if (response.contains("notification"))
continue;
if (response.value("id").toInt() == m_commandCounter) {
m_commandCounter++;
return QPair<QVariant, QWebSocket *>(jsonDoc.toVariant(), socket);
}
}
m_commandCounter++;
return QPair<QVariant, QWebSocket *>(QVariant(), socket);
}
void BaseTest::initTestCase()
{
qRegisterMetaType<RemoteProxyConnection::State>();

View File

@ -76,6 +76,9 @@ protected:
void loadConfiguration(const QString &fileName);
void setAuthenticator(Authenticator *authenticator);
void resetDebugCategories();
void addDebugCategory(const QString &debugCategory);
void cleanUpEngine();
void restartEngine();
void startEngine();
@ -94,6 +97,9 @@ protected:
QVariant invokeTcpSocketTunnelProxyApiCall(const QString &method, const QVariantMap params = QVariantMap(), bool remainsConnected = true);
QVariant injectTcpSocketTunnelProxyData(const QByteArray &data);
QPair<QVariant, QSslSocket *> invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), QSslSocket *existingSocket = nullptr);
QPair<QVariant, QWebSocket *> invokeWebSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), QWebSocket *existingSocket = nullptr);
bool createRemoteConnection(const QString &token, const QString &nonce, QObject *parent);
@ -137,6 +143,13 @@ public slots:
verifyError(response, "authenticationError", JsonTypes::authenticationErrorToString(error));
}
inline void verifyTunnelProxyError(const QVariant &response, TunnelProxyServer::TunnelProxyError error = TunnelProxyServer::TunnelProxyErrorNoError) {
verifyError(response, "tunnelProxyError", JsonTypes::tunnelProxyErrorToString(error));
}
private:
QString m_defaultDebugCategories = "*.debug=false\ndefault.debug=true\nApplication.debug=true\n";
QString m_currentDebugCategories;
};
#endif // BASETEST_H