From 56d32bed2bbd98ab9e37384842dd87ed35974db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 29 Jul 2021 19:04:44 +0200 Subject: [PATCH] Add server and client registration and prepare client data multiplexing --- libnymea-remoteproxy/engine.cpp | 12 +- libnymea-remoteproxy/engine.h | 6 +- .../jsonrpc/authenticationhandler.cpp | 2 +- libnymea-remoteproxy/jsonrpc/jsontypes.cpp | 2 +- libnymea-remoteproxy/jsonrpc/jsontypes.h | 4 +- .../jsonrpc/tunnelproxyhandler.cpp | 72 +++++- .../jsonrpc/tunnelproxyhandler.h | 6 +- libnymea-remoteproxy/libnymea-remoteproxy.pro | 6 +- libnymea-remoteproxy/loggingcategories.cpp | 3 +- libnymea-remoteproxy/loggingcategories.h | 5 +- libnymea-remoteproxy/proxy/proxyclient.cpp | 20 -- libnymea-remoteproxy/proxy/proxyclient.h | 9 - libnymea-remoteproxy/server/jsonrpcserver.cpp | 1 + .../server/transportclient.cpp | 20 ++ libnymea-remoteproxy/server/transportclient.h | 11 + .../tunnelproxy/tunnelproxyclient.cpp | 62 +++++ .../tunnelproxy/tunnelproxyclient.h | 41 +++ .../tunnelproxyclientconnection.cpp | 28 ++- .../tunnelproxy/tunnelproxyclientconnection.h | 18 +- .../tunnelproxy/tunnelproxymanager.cpp | 163 ------------ .../tunnelproxy/tunnelproxyserver.cpp | 238 ++++++++++++++++++ ...nnelproxymanager.h => tunnelproxyserver.h} | 41 +-- .../tunnelproxyserverconnection.cpp | 9 +- .../tunnelproxy/tunnelproxyserverconnection.h | 13 +- .../remoteproxyteststunnelproxy.cpp | 136 ++++++++++ .../remoteproxyteststunnelproxy.h | 6 + tests/testbase/basetest.cpp | 147 ++++++++++- tests/testbase/basetest.h | 13 + 28 files changed, 837 insertions(+), 257 deletions(-) create mode 100644 libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.cpp create mode 100644 libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.h delete mode 100644 libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.cpp create mode 100644 libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp rename libnymea-remoteproxy/tunnelproxy/{tunnelproxymanager.h => tunnelproxyserver.h} (67%) diff --git a/libnymea-remoteproxy/engine.cpp b/libnymea-remoteproxy/engine.cpp index 9db9396..bf0b1dc 100644 --- a/libnymea-remoteproxy/engine.cpp +++ b/libnymea-remoteproxy/engine.cpp @@ -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 diff --git a/libnymea-remoteproxy/engine.h b/libnymea-remoteproxy/engine.h index 9fc5264..6d55e6d 100644 --- a/libnymea-remoteproxy/engine.h +++ b/libnymea-remoteproxy/engine.h @@ -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; diff --git a/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp b/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp index 0b0c9ad..1682446 100644 --- a/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp +++ b/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp @@ -63,7 +63,7 @@ QString AuthenticationHandler::name() const JsonReply *AuthenticationHandler::Authenticate(const QVariantMap ¶ms, 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(); diff --git a/libnymea-remoteproxy/jsonrpc/jsontypes.cpp b/libnymea-remoteproxy/jsonrpc/jsontypes.cpp index 84f42b8..a91d19f 100644 --- a/libnymea-remoteproxy/jsonrpc/jsontypes.cpp +++ b/libnymea-remoteproxy/jsonrpc/jsontypes.cpp @@ -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; } diff --git a/libnymea-remoteproxy/jsonrpc/jsontypes.h b/libnymea-remoteproxy/jsonrpc/jsontypes.h index 4b85732..bab2c1f 100644 --- a/libnymea-remoteproxy/jsonrpc/jsontypes.h +++ b/libnymea-remoteproxy/jsonrpc/jsontypes.h @@ -33,7 +33,7 @@ #include #include -#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 diff --git a/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp index a1abc38..c753a0f 100644 --- a/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp +++ b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp @@ -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 ¶ms, 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 ¶ms, 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 ¶ms, TransportClient *transportClient) +//{ + +//} + } diff --git a/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h index b841625..0265e21 100644 --- a/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h +++ b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h @@ -46,9 +46,13 @@ public: QString name() const override; Q_INVOKABLE JsonReply *RegisterServer(const QVariantMap ¶ms, TransportClient *transportClient); + Q_INVOKABLE JsonReply *RegisterClient(const QVariantMap ¶ms, TransportClient *transportClient); + +// Q_INVOKABLE JsonReply *RemoveClient(const QVariantMap ¶ms, TransportClient *transportClient); signals: - + void ClientConnected(const QVariantMap ¶ms, TransportClient *transportClient); + void ClientDisconnected(const QVariantMap ¶ms, TransportClient *transportClient); }; diff --git a/libnymea-remoteproxy/libnymea-remoteproxy.pro b/libnymea-remoteproxy/libnymea-remoteproxy.pro index b050232..7ebe9f4 100644 --- a/libnymea-remoteproxy/libnymea-remoteproxy.pro +++ b/libnymea-remoteproxy/libnymea-remoteproxy.pro @@ -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 diff --git a/libnymea-remoteproxy/loggingcategories.cpp b/libnymea-remoteproxy/loggingcategories.cpp index 6276313..d470a3f 100644 --- a/libnymea-remoteproxy/loggingcategories.cpp +++ b/libnymea-remoteproxy/loggingcategories.cpp @@ -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") diff --git a/libnymea-remoteproxy/loggingcategories.h b/libnymea-remoteproxy/loggingcategories.h index 2c4addd..ea7b3ac 100644 --- a/libnymea-remoteproxy/loggingcategories.h +++ b/libnymea-remoteproxy/loggingcategories.h @@ -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) diff --git a/libnymea-remoteproxy/proxy/proxyclient.cpp b/libnymea-remoteproxy/proxy/proxyclient.cpp index 97d071a..7f170a3 100644 --- a/libnymea-remoteproxy/proxy/proxyclient.cpp +++ b/libnymea-remoteproxy/proxy/proxyclient.cpp @@ -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; diff --git a/libnymea-remoteproxy/proxy/proxyclient.h b/libnymea-remoteproxy/proxy/proxyclient.h index ab184dd..529d99e 100644 --- a/libnymea-remoteproxy/proxy/proxyclient.h +++ b/libnymea-remoteproxy/proxy/proxyclient.h @@ -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; diff --git a/libnymea-remoteproxy/server/jsonrpcserver.cpp b/libnymea-remoteproxy/server/jsonrpcserver.cpp index 358c4dc..acc39e3 100644 --- a/libnymea-remoteproxy/server/jsonrpcserver.cpp +++ b/libnymea-remoteproxy/server/jsonrpcserver.cpp @@ -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)) { diff --git a/libnymea-remoteproxy/server/transportclient.cpp b/libnymea-remoteproxy/server/transportclient.cpp index 1969403..0e108ac 100644 --- a/libnymea-remoteproxy/server/transportclient.cpp +++ b/libnymea-remoteproxy/server/transportclient.cpp @@ -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; diff --git a/libnymea-remoteproxy/server/transportclient.h b/libnymea-remoteproxy/server/transportclient.h index 17209dd..a43e746 100644 --- a/libnymea-remoteproxy/server/transportclient.h +++ b/libnymea-remoteproxy/server/transportclient.h @@ -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 diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.cpp new file mode 100644 index 0000000..322413d --- /dev/null +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.cpp @@ -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 TunnelProxyClient::processData(const QByteArray &data) +{ + // TODO: unescape if this data is for the json handler + QList 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(); + +} + +} diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.h b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.h new file mode 100644 index 0000000..832be0a --- /dev/null +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.h @@ -0,0 +1,41 @@ +#ifndef TUNNELPROXYCLIENT_H +#define TUNNELPROXYCLIENT_H + +#include + +#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 processData(const QByteArray &data) override; + +signals: + void typeChanged(Type type); + +private: + Type m_type = TypeNone; + +}; + +QDebug operator<< (QDebug debug, TunnelProxyClient *tunnelProxyClient); + +} + +#endif // TUNNELPROXYCLIENT_H diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.cpp index 1b9d334..db078c6 100644 --- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.cpp +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.cpp @@ -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; +} + } diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.h b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.h index 4b095d4..56086d8 100644 --- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.h +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.h @@ -28,18 +28,34 @@ #ifndef TUNNELPROXYCLIENTCONNECTION_H #define TUNNELPROXYCLIENTCONNECTION_H +#include #include 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; + }; } diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.cpp deleted file mode 100644 index 315eccb..0000000 --- a/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.cpp +++ /dev/null @@ -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 . -* -* 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(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(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); -} - -} diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp new file mode 100644 index 0000000..920a823 --- /dev/null +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp @@ -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 . +* +* 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(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(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); +} + +} diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.h b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.h similarity index 67% rename from libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.h rename to libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.h index 6b860ba..585d229 100644 --- a/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.h +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.h @@ -25,37 +25,43 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef TUNNELPROXYMANAGER_H -#define TUNNELPROXYMANAGER_H +#ifndef TUNNELPROXYSERVER_H +#define TUNNELPROXYSERVER_H #include #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 m_transportInterfaces; bool m_running = false; - QHash m_proxyClients; // clients + QHash m_proxyClients; // clientId, object // Server connections - QHash m_proxyClientsTunnelServer; // clientUuid, object - QHash m_tunnelServers; // server uuid, object + QHash m_tunnelProxyServerConnections; // server uuid, object + QHash m_tunnelProxyClientConnections; // client uuid, object }; } -#endif // TUNNELPROXYMANAGER_H +#endif // TUNNELPROXYSERVER_H diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.cpp index cfb76f1..3f190fa 100644 --- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.cpp +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.cpp @@ -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 diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.h b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.h index 46328f3..2293930 100644 --- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.h +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.h @@ -28,27 +28,30 @@ #ifndef TUNNELPROXYSERVERCONNECTION_H #define TUNNELPROXYSERVERCONNECTION_H +#include #include -#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; diff --git a/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp index cc48316..725eee7 100644 --- a/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp +++ b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp @@ -199,4 +199,140 @@ void RemoteProxyTestsTunnelProxy::apiBasicCalls() stopServer(); } + +void RemoteProxyTestsTunnelProxy::registerServer_data() +{ + QTest::addColumn("name"); + QTest::addColumn("uuid"); + QTest::addColumn("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 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 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) diff --git a/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h index ceea833..3a4af35 100644 --- a/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h +++ b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h @@ -54,6 +54,12 @@ private slots: void apiBasicCallsTcp_data(); void apiBasicCallsTcp(); + void registerServer_data(); + void registerServer(); + + void registerServerDuplicated(); + + }; #endif // REMOTEPROXYTESTSTUNNELPROXY_H diff --git a/tests/testbase/basetest.cpp b/tests/testbase/basetest.cpp index 22d8341..2771bd1 100644 --- a/tests/testbase/basetest.cpp +++ b/tests/testbase/basetest.cpp @@ -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 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 &); + QObject::connect(socket, static_cast(&QSslSocket::sslErrors), this, &BaseTest::sslSocketSslErrors); + + QSignalSpy spyConnection(socket, &QSslSocket::connected); + socket->connectToHostEncrypted(Engine::instance()->tcpSocketServerTunnelProxy()->serverUrl().host(), + static_cast(Engine::instance()->tcpSocketServerTunnelProxy()->serverUrl().port())); + spyConnection.wait(); + if (spyConnection.count() == 0) { + return QPair(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(), 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(), 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(), socket); + } + + QVariantMap response = jsonDoc.toVariant().toMap(); + + if (response.value("id").toInt() == m_commandCounter) { + m_commandCounter++; + return QPair(jsonDoc.toVariant(), socket); + } + + m_commandCounter++; + return QPair(QVariant(), socket); +} + +QPair 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(), 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(), 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(), socket); + } + QVariantMap response = jsonDoc.toVariant().toMap(); + + // skip notifications + if (response.contains("notification")) + continue; + + if (response.value("id").toInt() == m_commandCounter) { + m_commandCounter++; + return QPair(jsonDoc.toVariant(), socket); + } + } + + m_commandCounter++; + return QPair(QVariant(), socket); +} + void BaseTest::initTestCase() { qRegisterMetaType(); diff --git a/tests/testbase/basetest.h b/tests/testbase/basetest.h index a68f174..7734b4a 100644 --- a/tests/testbase/basetest.h +++ b/tests/testbase/basetest.h @@ -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 invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), QSslSocket *existingSocket = nullptr); + QPair 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