diff --git a/README.md b/README.md index a6806eb..12b207e 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ In order to get information about the server you can start the command with the Version: 0.1.5 API version: 0.3 - Copyright © 2018 Simon Stürz + Copyright © 2018 Simon Stürz Options: @@ -131,7 +131,7 @@ In order to get information about the server you can start the command with the --verbose Print more verbose. -# Server API +# Proxy server API Once a client connects to the proxy server, he must authenticate him self by passing the token received from the nymea-cloud mqtt connection request. @@ -208,6 +208,9 @@ If anything goes wrong, or the tunnel partner disconnects from the proxy, the se "status": "success" } + +# Proxy server + ## Authenticate the connection The first data a client **must** send to the proxy server is the authentication request. This request contains the `token` which will be verified agains the nymea-cloud infrastructure and a `nonce` which has to be uniq for each connection attempt and shared between the 2 clients. The `uuid` should be a persistant uuid for this client and the name should make clear which type of connection this is and which client is connecting. The name and uuid will be sent to the tunnel partner during the tunnel establishmend. @@ -270,76 +273,82 @@ Once the other client is here and ready, the server will send a notification to #### Response - "id": 1, - "params": { - "methods": { - "Authentication.Authenticate": { - "description": "Authenticate this connection. The returned AuthenticationError informs about the result. If the authentication was not successfull, the server will close the connection immediatly after sending the error response. The given id should be a unique id the other tunnel client can understand. Once the authentication was successfull, you can wait for the RemoteProxy.TunnelEstablished notification. If you send any data before getting this notification, the server will close the connection. If the tunnel client does not show up within 10 seconds, the server will close the connection.", - "params": { - "name": "String", - "o:nonce": "String", - "token": "String", - "uuid": "String" + { + "id": 1, + "params": { + "methods": { + "Authentication.Authenticate": { + "description": "Authenticate this connection. The returned AuthenticationError informs about the result. If the authentication was not successfull, the server will close the connection immediatly after sending the error response. The given id should be a unique id the other tunnel client can understand. Once the authentication was successfull, you can wait for the RemoteProxy.TunnelEstablished notification. If you send any data before getting this notification, the server will close the connection. If the tunnel client does not show up within 10 seconds, the server will close the connection.", + "params": { + "name": "String", + "o:nonce": "String", + "token": "String", + "uuid": "String" + }, + "returns": { + "authenticationError": "$ref:AuthenticationError" + } }, - "returns": { - "authenticationError": "$ref:AuthenticationError" + "RemoteProxy.Hello": { + "description": "Once connected to this server, a client can get information about the server by saying Hello. The response informs the client about this proxy server.", + "params": { + }, + "returns": { + "apiVersion": "String", + "name": "String", + "server": "String", + "version": "String" + } + }, + "RemoteProxy.Introspect": { + "description": "Introspect this API.", + "params": { + }, + "returns": { + "methods": "Object", + "notifications": "Object", + "types": "Object" + } } }, - "RemoteProxy.Hello": { - "description": "Once connected to this server, a client can get information about the server by saying Hello. The response informs the client about this proxy server.", - "params": { - }, - "returns": { - "apiVersion": "String", - "name": "String", - "server": "String", - "version": "String" + "notifications": { + "RemoteProxy.TunnelEstablished": { + "description": "Emitted whenever the tunnel has been established successfully. This is the last message from the remote proxy server! Any following data will be from the other tunnel client until the connection will be closed. The parameter contain some information about the other tunnel client.", + "params": { + "name": "String", + "uuid": "String" + } } }, - "RemoteProxy.Introspect": { - "description": "Introspect this API.", - "params": { - }, - "returns": { - "methods": "Object", - "notifications": "Object", - "types": "Object" - } + "types": { + "AuthenticationError": [ + "AuthenticationErrorNoError", + "AuthenticationErrorUnknown", + "AuthenticationErrorTimeout", + "AuthenticationErrorAborted", + "AuthenticationErrorAuthenticationFailed", + "AuthenticationErrorProxyError" + ], + "BasicType": [ + "Uuid", + "String", + "Int", + "UInt", + "Double", + "Bool", + "Variant", + "Object" + ] } }, - "notifications": { - "RemoteProxy.TunnelEstablished": { - "description": "Emitted whenever the tunnel has been established successfully. This is the last message from the remote proxy server! Any following data will be from the other tunnel client until the connection will be closed. The parameter contain some information about the other tunnel client.", - "params": { - "name": "String", - "uuid": "String" - } - } - }, - "types": { - "AuthenticationError": [ - "AuthenticationErrorNoError", - "AuthenticationErrorUnknown", - "AuthenticationErrorTimeout", - "AuthenticationErrorAborted", - "AuthenticationErrorAuthenticationFailed", - "AuthenticationErrorProxyError" - ], - "BasicType": [ - "Uuid", - "String", - "Int", - "UInt", - "Double", - "Bool", - "Variant", - "Object" - ] - } - }, - "status": "success" -} + "status": "success" + } + +# Proxy server + + + # Server monitor The server provides a live monitor interface on a local socket server. You can follow the monitor data with: @@ -358,7 +367,7 @@ There is also the package `nymea-remoteproxy-monitor` package and application wh Server version: 0.1.5 API version: 0.3 - Copyright © 2018 Simon Stürz + Copyright © 2018 Simon Stürz Options: @@ -384,7 +393,7 @@ The client allowes you to test the proxy server and create a dummy client for te Version: 0.1.5 API version: 0.3 - Copyright © 2018 Simon Stürz + Copyright © 2018 Simon Stürz Options: diff --git a/debian/control b/debian/control index 38af69e..7d6d42e 100644 --- a/debian/control +++ b/debian/control @@ -1,12 +1,13 @@ Source: nymea-remoteproxy Section: utils Priority: options -Maintainer: Simon Stürz +Maintainer: Simon Stürz Build-depends: debhelper (>= 0.0.0), libqt5websockets5-dev, libncurses5-dev, Standards-Version: 3.9.3 + Package: nymea-remoteproxy Architecture: any Depends: ${shlibs:Depends}, @@ -17,6 +18,7 @@ Suggests: nymea-remoteproxy-monitor (= ${binary:Version}) Description: The nymea remote proxy server The nymea remote proxy server + Package: libnymea-remoteproxy Architecture: any Section: libs @@ -25,6 +27,7 @@ Depends: ${shlibs:Depends}, Description: The nymea remote proxy server lib The nymea remote proxy server lib + Package: libnymea-remoteproxy-dev Architecture: any Section: libdevel @@ -34,15 +37,6 @@ Depends: ${shlibs:Depends}, Description: The nymea remote proxy server lib - development files The nymea remote proxy server lib - development files -Package: libnymea-remoteproxy-dbg -Architecture: any -Section: debug -Depends: ${shlibs:Depends}, - ${misc:Depends}, - libnymea-remoteproxy (= ${binary:Version}), -Description: The nymea remote proxy server lib - debug symbols - The nymea remote proxy server lib - debug symbols - Package: nymea-remoteproxy-client Architecture: any @@ -52,6 +46,7 @@ Depends: ${shlibs:Depends}, Description: The nymea remote proxy client for testing The nymea remote proxy client for testing + Package: libnymea-remoteproxyclient Architecture: any Section: libs @@ -60,6 +55,7 @@ Depends: ${shlibs:Depends}, Description: The nymea remote proxy server client lib The nymea remote proxy server client lib + Package: libnymea-remoteproxyclient-dev Architecture: any Section: libdevel @@ -69,14 +65,6 @@ Depends: ${shlibs:Depends}, Description: The nymea remote proxy server client lib - development files The nymea remote proxy server client lib - development files -Package: libnymea-remoteproxyclient-dbg -Architecture: any -Section: debug -Depends: ${shlibs:Depends}, - ${misc:Depends}, - libnymea-remoteproxyclient (= ${binary:Version}), -Description: The nymea remote proxy server client lib - debug symbols - The nymea remote proxy server client lib - debug symbols Package: nymea-remoteproxy-tests Architecture: any @@ -86,6 +74,7 @@ Depends: ${shlibs:Depends}, Description: The nymea remote proxy server tests The nymea remote proxy server tests + Package: nymea-remoteproxy-monitor Architecture: any Depends: ${shlibs:Depends}, diff --git a/debian/copyright b/debian/copyright index be434ed..6e741f3 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,10 +1,9 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: nymea-remoteproxy -Upstream-Contact: Simon Stürz -Copyright: 2018, guh GmbH -Download: http://www.github.com/guh/nymea-remoteproxy -Source: https://github.com/guh/nymea-remoteproxy.git - +Upstream-Contact: Simon Stürz +Copyright: 2018 - 2021, nymea GmbH +Download: http://www.github.com/nymea/nymea-remoteproxy +Source: https://github.com/nymea/nymea-remoteproxy.git License: GPL-3 On Debian systems, the complete text of the GNU General @@ -17,20 +16,24 @@ License: LGPL-3 Files: libnymea-remoteproxyclient/* License: LGPL-3 -Copyright: 2018 - 2010, nymea GmbH +Copyright: 2018 - 2021, nymea GmbH Files: libnymea-remoteproxy/* License: GPL-3 -Copyright: 2018 - 2010, nymea GmbH +Copyright: 2018 - 2021, nymea GmbH Files: client/* License: GPL-3 -Copyright: 2018 - 2010, nymea GmbH +Copyright: 2018 - 2021, nymea GmbH Files: monitor/* License: GPL-3 -Copyright: 2018 - 2010, nymea GmbH +Copyright: 2018 - 2021, nymea GmbH Files: server/* License: GPL-3 -Copyright: 2018 - 2010, nymea GmbH +Copyright: 2018 - 2021, nymea GmbH + +Files: tests/* +License: GPL-3 +Copyright: 2018 - 2021, nymea GmbH diff --git a/debian/nymea-remoteproxy-tests.install b/debian/nymea-remoteproxy-tests.install index 059a6b9..bec587f 100644 --- a/debian/nymea-remoteproxy-tests.install +++ b/debian/nymea-remoteproxy-tests.install @@ -1 +1,2 @@ -usr/bin/nymea-remoteproxy-tests-offline +usr/bin/nymea-remoteproxy-proxy-tests +usr/bin/nymea-remoteproxy-tunnelproxy-tests diff --git a/docs/remote-connection-basic-flow.png b/docs/remote-connection-basic-flow.png new file mode 100644 index 0000000..11b041a Binary files /dev/null and b/docs/remote-connection-basic-flow.png differ diff --git a/docs/remote-connection-basic-flow.svg b/docs/remote-connection-basic-flow.svg new file mode 100644 index 0000000..f8a29ea --- /dev/null +++ b/docs/remote-connection-basic-flow.svg @@ -0,0 +1 @@ +title%20Remote%20tunnel%20proxy%0A%0Anymea-%3Eproxy%3A%20RegisterProxyTunnelServer(uuid%2C%20name)%0A%0Anote%20over%20proxy%3A%20Register%20the%20server%20using%20the%20uuid%0A%0Anymea%3C-proxy%3A%20Success%0A%0Anote%20over%20nymea%2C%20proxy%3A%20Protocol%20from%20now%20on%20SLIP%5CnThe%20proxy%20is%20client%200x0000%0A%0Anote%20over%20nymea%2C%20proxy%3A%20SLIP%20encoded%20data%3A%202%20Bytes%20address%20%2B%20data%0A%0Aproxy%3C-client%3A%20ConnectProxyTunnelClient(uuid%2C%20name%2C%20serverUuid)%0A%0Anote%20over%20proxy%3A%20Search%20server%20using%20uuid%0A%0Anote%20over%20proxy%3A%20Server%3A%20Assign%20address%200x0001%20for%20this%20client%20socket%0A%0Aproxy-%3Enymea%3A%20SLIP%3A0x0000%3A%20ProxyTunnelClientConnected%20(address%3A%200x0001)%0A%0Aproxy%3C-nymea%3A%20SLIP%3A0x0000%3A%20AckProxyTunnelClient%20(address%3A%200x0001)%0A%0Aproxy-%3Eclient%3A%20ProxyTunnelEstablished(uuid%2C%20name%2C%20serverUuid)%0A%0Anote%20over%20proxy%2C%20client%3A%20Protocol%20from%20now%20on%20SLIP%20encoded%5CnThe%20proxy%20is%20client%200x0000%5CnThe%20connected%20server%20is%200xFFFF%0A%0Anote%20over%20proxy%2C%20client%3A%20SLIP%20encoded%20data%3A%202%20Bytes%20address%20%2B%20data%0A%0Anote%20over%20nymea%2C%20client%3A%20Connected%3A%20The%20client%20can%20now%20communicate%20with%20nymea%20directly.%0A%0Anote%20over%20nymea%2C%20client%3A%20nymea%20sends%20SLIP%20encoded%20data%20with%20address%200x0001%20-%3E%20client%0A%0Anote%20over%20nymea%2C%20client%3A%20client%20sends%20SLIP%20endcoded%20data%20with%20address%200xFFFF%20-%3E%20nymeaRemote tunnel proxynymeaproxyclientRegisterProxyTunnelServer(uuid, name)Register the server using the uuidSuccessProtocol from now on SLIPThe proxy is client 0x0000SLIP encoded data: 2 Bytes address + dataConnectProxyTunnelClient(uuid, name, serverUuid)Search server using uuidServer: Assign address 0x0001 for this client socketSLIP:0x0000: ProxyTunnelClientConnected (address: 0x0001)SLIP:0x0000: AckProxyTunnelClient (address: 0x0001)ProxyTunnelEstablished(uuid, name, serverUuid)Protocol from now on SLIP encodedThe proxy is client 0x0000The connected server is 0xFFFFSLIP encoded data: 2 Bytes address + dataConnected: The client can now communicate with nymea directly.nymea sends SLIP encoded data with address 0x0001 -> clientclient sends SLIP endcoded data with address 0xFFFF -> nymea \ No newline at end of file diff --git a/docs/remote-connection-basic-flow.txt b/docs/remote-connection-basic-flow.txt new file mode 100644 index 0000000..24bb29e --- /dev/null +++ b/docs/remote-connection-basic-flow.txt @@ -0,0 +1,33 @@ +title Remote tunnel proxy + +nymea->proxy: RegisterProxyTunnelServer(uuid, name) + +note over proxy: Register the server using the uuid + +nymea<-proxy: Success + +note over nymea, proxy: Protocol from now on SLIP\nThe proxy is client 0x0000 + +note over nymea, proxy: SLIP encoded data: 2 Bytes address + data + +proxy<-client: ConnectProxyTunnelClient(uuid, name, serverUuid) + +note over proxy: Search server using uuid + +note over proxy: Server: Assign address 0x0001 for this client socket + +proxy->nymea: SLIP:0x0000: ProxyTunnelClientConnected (address: 0x0001) + +proxy<-nymea: SLIP:0x0000: AckProxyTunnelClient (address: 0x0001) + +proxy->client: ProxyTunnelEstablished(uuid, name, serverUuid) + +note over proxy, client: Protocol from now on SLIP encoded\nThe proxy is client 0x0000\nThe connected server is 0xFFFF + +note over proxy, client: SLIP encoded data: 2 Bytes address + data + +note over nymea, client: Connected: The client can now communicate with nymea directly. + +note over nymea, client: nymea sends SLIP encoded data with address 0x0001 -> client + +note over nymea, client: client sends SLIP endcoded data with address 0xFFFF -> nymea diff --git a/libnymea-remoteproxy/engine.cpp b/libnymea-remoteproxy/engine.cpp index 82bee33..5cd1a8c 100644 --- a/libnymea-remoteproxy/engine.cpp +++ b/libnymea-remoteproxy/engine.cpp @@ -71,32 +71,62 @@ void Engine::start(ProxyConfiguration *configuration) // Make sure an authenticator was registered Q_ASSERT_X(m_authenticator != nullptr, "Engine", "There is no authenticator registerd."); + // Proxy + // ------------------------------------- m_proxyServer = new ProxyServer(this); - m_webSocketServer = new WebSocketServer(m_configuration->sslEnabled(), m_configuration->sslConfiguration(), this); - m_tcpSocketServer = new TcpSocketServer(m_configuration->sslEnabled(), m_configuration->sslConfiguration(), this); + m_webSocketServerProxy = new WebSocketServer(m_configuration->sslEnabled(), m_configuration->sslConfiguration(), this); + m_tcpSocketServerProxy = new TcpSocketServer(m_configuration->sslEnabled(), m_configuration->sslConfiguration(), this); // Configure websocket server QUrl websocketServerUrl; websocketServerUrl.setScheme(m_configuration->sslEnabled() ? "wss" : "ws"); - websocketServerUrl.setHost(m_configuration->webSocketServerHost().toString()); - websocketServerUrl.setPort(m_configuration->webSocketServerPort()); - m_webSocketServer->setServerUrl(websocketServerUrl); + websocketServerUrl.setHost(m_configuration->webSocketServerProxyHost().toString()); + websocketServerUrl.setPort(m_configuration->webSocketServerProxyPort()); + m_webSocketServerProxy->setServerUrl(websocketServerUrl); // Configure tcp socket server - QUrl tcpSocketServerUrl; - tcpSocketServerUrl.setScheme(m_configuration->sslEnabled() ? "ssl" : "tcp"); - tcpSocketServerUrl.setHost(m_configuration->tcpServerHost().toString()); - tcpSocketServerUrl.setPort(m_configuration->tcpServerPort()); - m_tcpSocketServer->setServerUrl(tcpSocketServerUrl); + QUrl tcpSocketServerProxyUrl; + tcpSocketServerProxyUrl.setScheme(m_configuration->sslEnabled() ? "ssl" : "tcp"); + tcpSocketServerProxyUrl.setHost(m_configuration->tcpServerHost().toString()); + tcpSocketServerProxyUrl.setPort(m_configuration->tcpServerPort()); + m_tcpSocketServerProxy->setServerUrl(tcpSocketServerProxyUrl); // Register the transport interfaces in the proxy server - m_proxyServer->registerTransportInterface(m_webSocketServer); - m_proxyServer->registerTransportInterface(m_tcpSocketServer); + m_proxyServer->registerTransportInterface(m_webSocketServerProxy); + m_proxyServer->registerTransportInterface(m_tcpSocketServerProxy); // Start the server - qCDebug(dcEngine()) << "Starting proxy server"; + qCDebug(dcEngine()) << "Starting the proxy servers..."; m_proxyServer->startServer(); + // Tunnel proxy + // ------------------------------------- + m_tunnelProxyManager = new TunnelProxyManager(this); + m_webSocketServerTunnelProxy = new WebSocketServer(m_configuration->sslEnabled(), m_configuration->sslConfiguration(), this); + m_tcpSocketServerTunnelProxy = new TcpSocketServer(m_configuration->sslEnabled(), m_configuration->sslConfiguration(), this); + + // Configure websocket server + QUrl websocketServerTunnelProxyUrl; + websocketServerTunnelProxyUrl.setScheme(m_configuration->sslEnabled() ? "wss" : "ws"); + websocketServerTunnelProxyUrl.setHost(m_configuration->webSocketServerTunnelProxyHost().toString()); + websocketServerTunnelProxyUrl.setPort(m_configuration->webSocketServerTunnelProxyPort()); + m_webSocketServerTunnelProxy->setServerUrl(websocketServerTunnelProxyUrl); + + // Configure tcp socket server + QUrl tcpSocketServerTunnelProxyUrl; + tcpSocketServerTunnelProxyUrl.setScheme(m_configuration->sslEnabled() ? "ssl" : "tcp"); + tcpSocketServerTunnelProxyUrl.setHost(m_configuration->tcpServerTunnelProxyHost().toString()); + tcpSocketServerTunnelProxyUrl.setPort(m_configuration->tcpServerTunnelProxyPort()); + m_tcpSocketServerTunnelProxy->setServerUrl(tcpSocketServerTunnelProxyUrl); + + // Register the transport interfaces in the proxy server + m_tunnelProxyManager->registerTransportInterface(m_webSocketServerTunnelProxy); + m_tunnelProxyManager->registerTransportInterface(m_tcpSocketServerTunnelProxy); + + // Start the server + qCDebug(dcEngine()) << "Starting the tunnel proxy manager..."; + m_tunnelProxyManager->startServer(); + // Start the monitor server m_monitorServer = new MonitorServer(configuration->monitorSocketFileName(), this); m_monitorServer->startServer(); @@ -165,14 +195,29 @@ ProxyServer *Engine::proxyServer() const return m_proxyServer; } -TcpSocketServer *Engine::tcpSocketServer() const +TunnelProxyManager *Engine::tunnelProxyManager() const { - return m_tcpSocketServer; + return m_tunnelProxyManager; } -WebSocketServer *Engine::webSocketServer() const +TcpSocketServer *Engine::tcpSocketServerProxy() const { - return m_webSocketServer; + return m_tcpSocketServerProxy; +} + +WebSocketServer *Engine::webSocketServerProxy() const +{ + return m_webSocketServerProxy; +} + +TcpSocketServer *Engine::tcpSocketServerTunnelProxy() const +{ + return m_tcpSocketServerTunnelProxy; +} + +WebSocketServer *Engine::webSocketServerTunnelProxy() const +{ + return m_webSocketServerTunnelProxy; } MonitorServer *Engine::monitorServer() const @@ -188,7 +233,7 @@ LogEngine *Engine::logEngine() const Engine::Engine(QObject *parent) : QObject(parent) { - m_lastTimeStamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); + m_lastTimeStamp = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(); m_timer = new QTimer(this); m_timer->setSingleShot(false); @@ -216,7 +261,7 @@ QVariantMap Engine::createServerStatistic() void Engine::onTimerTick() { - qint64 timestamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qint64 timestamp = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(); qint64 deltaTime = timestamp - m_lastTimeStamp; m_lastTimeStamp = timestamp; @@ -249,9 +294,9 @@ void Engine::clean() m_proxyServer = nullptr; } - if (m_webSocketServer) { - delete m_webSocketServer; - m_webSocketServer = nullptr; + if (m_webSocketServerProxy) { + delete m_webSocketServerProxy; + m_webSocketServerProxy = nullptr; } if (m_configuration) { @@ -265,6 +310,9 @@ void Engine::setRunning(bool running) if (m_running == running) return; + if (m_proxyServer) + m_proxyServer->setRunning(running); + qCDebug(dcEngine()) << "Engine is" << (running ? "now running." : "not running any more."); if (running) { diff --git a/libnymea-remoteproxy/engine.h b/libnymea-remoteproxy/engine.h index 849ae6f..9fc5264 100644 --- a/libnymea-remoteproxy/engine.h +++ b/libnymea-remoteproxy/engine.h @@ -36,12 +36,14 @@ #include #include "logengine.h" -#include "proxyserver.h" -#include "monitorserver.h" -#include "tcpsocketserver.h" -#include "websocketserver.h" +#include "proxy/proxyserver.h" #include "proxyconfiguration.h" +#include "server/monitorserver.h" +#include "server/jsonrpcserver.h" +#include "server/tcpsocketserver.h" +#include "server/websocketserver.h" #include "authentication/authenticator.h" +#include "tunnelproxy/tunnelproxymanager.h" namespace remoteproxy { @@ -67,9 +69,17 @@ public: ProxyConfiguration *configuration() const; Authenticator *authenticator() const; + ProxyServer *proxyServer() const; - TcpSocketServer *tcpSocketServer() const; - WebSocketServer *webSocketServer() const; + TunnelProxyManager *tunnelProxyManager() const; + + TcpSocketServer *tcpSocketServerProxy() const; + WebSocketServer *webSocketServerProxy() const; + + TcpSocketServer *tcpSocketServerTunnelProxy() const; + WebSocketServer *webSocketServerTunnelProxy() const; + + MonitorServer *monitorServer() const; LogEngine *logEngine() const; @@ -89,9 +99,16 @@ private: ProxyConfiguration *m_configuration = nullptr; Authenticator *m_authenticator = nullptr; + ProxyServer *m_proxyServer = nullptr; - TcpSocketServer *m_tcpSocketServer = nullptr; - WebSocketServer *m_webSocketServer = nullptr; + TunnelProxyManager *m_tunnelProxyManager = nullptr; + + TcpSocketServer *m_tcpSocketServerProxy = nullptr; + WebSocketServer *m_webSocketServerProxy = nullptr; + + TcpSocketServer *m_tcpSocketServerTunnelProxy = nullptr; + WebSocketServer *m_webSocketServerTunnelProxy = nullptr; + MonitorServer *m_monitorServer = nullptr; LogEngine *m_logEngine = nullptr; diff --git a/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp b/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp index 57ab1fb..5e43cb1 100644 --- a/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp +++ b/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp @@ -106,7 +106,7 @@ void AuthenticationHandler::onAuthenticationFinished() jsonReply->setData(errorToReply(authenticationReply->error())); } - jsonReply->finished(); + emit jsonReply->finished(); } } diff --git a/libnymea-remoteproxy/jsonrpc/jsontypes.cpp b/libnymea-remoteproxy/jsonrpc/jsontypes.cpp index ce350ec..b54bf48 100644 --- a/libnymea-remoteproxy/jsonrpc/jsontypes.cpp +++ b/libnymea-remoteproxy/jsonrpc/jsontypes.cpp @@ -40,11 +40,11 @@ QString JsonTypes::s_lastError; // Types QVariantList JsonTypes::s_basicType; QVariantList JsonTypes::s_authenticationError; +QVariantList JsonTypes::s_tunnelProxyError; // Objects - QVariantMap JsonTypes::allTypes() { QVariantMap allTypes; @@ -52,6 +52,7 @@ QVariantMap JsonTypes::allTypes() // Enums allTypes.insert("BasicType", basicType()); allTypes.insert("AuthenticationError", authenticationError()); + allTypes.insert("TunnelProxyError", tunnelProxyError()); // Types @@ -63,6 +64,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_initialized = true; } @@ -124,10 +126,17 @@ QPair JsonTypes::validateVariant(const QVariant &templateVariant, qCWarning(dcJsonRpc()) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(authenticationErrorRef()); return result; } + } else if (refName == tunnelProxyErrorRef()) { + QPair result = validateEnum(s_tunnelProxyError, variant); + if (!result.first) { + qCWarning(dcJsonRpc()) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(tunnelProxyErrorRef()); + return result; + } } else { Q_ASSERT_X(false, "JsonTypes", QString("Unhandled ref: %1").arg(refName).toLatin1().data()); return report(false, QString("Unhandled ref %1. Server implementation incomplete.").arg(refName)); } + } else { QPair result = JsonTypes::validateProperty(templateVariant, variant); if (!result.first) { diff --git a/libnymea-remoteproxy/jsonrpc/jsontypes.h b/libnymea-remoteproxy/jsonrpc/jsontypes.h index 3ccd68a..4b85732 100644 --- a/libnymea-remoteproxy/jsonrpc/jsontypes.h +++ b/libnymea-remoteproxy/jsonrpc/jsontypes.h @@ -33,6 +33,7 @@ #include #include +#include "tunnelproxy/tunnelproxymanager.h" #include "authentication/authenticator.h" namespace remoteproxy { @@ -90,6 +91,7 @@ public: // Declare types DECLARE_TYPE(basicType, "BasicType", JsonTypes, BasicType) DECLARE_TYPE(authenticationError, "AuthenticationError", Authenticator, AuthenticationError) + DECLARE_TYPE(tunnelProxyError, "TunnelProxyError", TunnelProxyManager, Error) // Declare objects diff --git a/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp new file mode 100644 index 0000000..d896287 --- /dev/null +++ b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp @@ -0,0 +1,72 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 "tunnelproxyhandler.h" + +#include "engine.h" +#include "jsontypes.h" +#include "loggingcategories.h" + +#include "tunnelproxy/tunnelproxymanager.h" + +namespace remoteproxy { + +TunnelProxyHandler::TunnelProxyHandler(QObject *parent) : JsonHandler(parent) +{ + // Methods + QVariantMap params; QVariantMap returns; + + 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)); + setParams("RegisterServer", params); + returns.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorRef()); + setReturns("RegisterServer", returns); + +} + +QString TunnelProxyHandler::name() const +{ + return "TunnelProxy"; +} + +JsonReply *TunnelProxyHandler::RegisterServer(const QVariantMap ¶ms, ProxyClient *proxyClient) +{ + qCDebug(dcJsonRpc()) << name() << "register server" << params << proxyClient; + QUuid serverUuid = params.value("uuid").toUuid(); + QString serverName = params.value("name").toString(); + + TunnelProxyManager::Error error = Engine::instance()->tunnelProxyManager()->registerServer(proxyClient->clientId(), serverUuid, serverName); + + QVariantMap response; + response.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorToString(error)); + + return createReply("RegisterServer", response); +} + +} diff --git a/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h new file mode 100644 index 0000000..9fb29f4 --- /dev/null +++ b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h @@ -0,0 +1,55 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef TUNNELPROXYHANDLER_H +#define TUNNELPROXYHANDLER_H + +#include + +#include "jsonhandler.h" + +namespace remoteproxy { + +class TunnelProxyHandler : public JsonHandler +{ + Q_OBJECT +public: + explicit TunnelProxyHandler(QObject *parent = nullptr); + ~TunnelProxyHandler() override = default; + + QString name() const override; + + Q_INVOKABLE JsonReply *RegisterServer(const QVariantMap ¶ms, ProxyClient *proxyClient); + +signals: + + +}; + +} + +#endif // TUNNELPROXYHANDLER_H diff --git a/libnymea-remoteproxy/libnymea-remoteproxy.pro b/libnymea-remoteproxy/libnymea-remoteproxy.pro index 373a8e8..b5a179d 100644 --- a/libnymea-remoteproxy/libnymea-remoteproxy.pro +++ b/libnymea-remoteproxy/libnymea-remoteproxy.pro @@ -6,19 +6,22 @@ TARGET = nymea-remoteproxy HEADERS += \ engine.h \ loggingcategories.h \ - tcpsocketserver.h \ - transportinterface.h \ - websocketserver.h \ + tunnelproxy/tunnelproxymanager.h \ + tunnelproxy/tunnelproxyserver.h \ + server/tcpsocketserver.h \ + server/transportinterface.h \ + server/websocketserver.h \ + server/jsonrpcserver.h \ + server/monitorserver.h \ proxyclient.h \ - proxyserver.h \ - monitorserver.h \ + proxy/proxyserver.h \ + proxy/tunnelconnection.h \ proxyconfiguration.h \ - tunnelconnection.h \ - jsonrpcserver.h \ jsonrpc/jsonhandler.h \ jsonrpc/jsonreply.h \ jsonrpc/jsontypes.h \ jsonrpc/authenticationhandler.h \ + jsonrpc/tunnelproxyhandler.h \ authentication/authenticator.h \ authentication/authenticationreply.h \ authentication/dummy/dummyauthenticator.h \ @@ -32,19 +35,22 @@ HEADERS += \ SOURCES += \ engine.cpp \ loggingcategories.cpp \ - tcpsocketserver.cpp \ - transportinterface.cpp \ - websocketserver.cpp \ + tunnelproxy/tunnelproxymanager.cpp \ + tunnelproxy/tunnelproxyserver.cpp \ + server/tcpsocketserver.cpp \ + server/transportinterface.cpp \ + server/websocketserver.cpp \ + server/jsonrpcserver.cpp \ + server/monitorserver.cpp \ proxyclient.cpp \ - proxyserver.cpp \ - monitorserver.cpp \ + proxy/proxyserver.cpp \ + proxy/tunnelconnection.cpp \ proxyconfiguration.cpp \ - tunnelconnection.cpp \ - jsonrpcserver.cpp \ jsonrpc/jsonhandler.cpp \ jsonrpc/jsonreply.cpp \ jsonrpc/jsontypes.cpp \ jsonrpc/authenticationhandler.cpp \ + jsonrpc/tunnelproxyhandler.cpp \ authentication/authenticator.cpp \ authentication/authenticationreply.cpp \ authentication/dummy/dummyauthenticator.cpp \ @@ -57,7 +63,7 @@ SOURCES += \ # install header file with relative subdirectory -for(header, HEADERS) { +for (header, HEADERS) { path = $$[QT_INSTALL_PREFIX]/include/nymea-remoteproxy/$${dirname(header)} eval(headers_$${path}.files += $${header}) eval(headers_$${path}.path = $${path}) diff --git a/libnymea-remoteproxy/logengine.cpp b/libnymea-remoteproxy/logengine.cpp index 1a5d232..e547ff2 100644 --- a/libnymea-remoteproxy/logengine.cpp +++ b/libnymea-remoteproxy/logengine.cpp @@ -34,7 +34,7 @@ namespace remoteproxy { LogEngine::LogEngine(QObject *parent) : QObject(parent) { - m_currentDay = QDateTime::currentDateTime().date().day(); + m_currentDay = QDateTime::currentDateTimeUtc().date().day(); m_tunnelsFileName = "/var/log/nymea-remoteproxy-tunnels"; m_statisticsFileName = "/var/log/nymea-remoteproxy-statistics"; } @@ -79,9 +79,9 @@ void LogEngine::logStatistics(int tunnelCount, int connectionCount, int troughpu textStream << logString.join(" ") << endl; // Check if we have to rotate the logfile - if (m_currentDay != QDateTime::currentDateTime().date().day()) { + if (m_currentDay != QDateTime::currentDateTimeUtc().date().day()) { // Day changed - m_currentDay = QDateTime::currentDateTime().date().day(); + m_currentDay = QDateTime::currentDateTimeUtc().date().day(); rotateLogs(); } } @@ -98,7 +98,7 @@ void LogEngine::rotateLogs() m_statisticsFile.close(); // Rename the current files - QString postfix = "-" + QDateTime::currentDateTime().toString("yyyyMMddhhmmss") + ".log"; + QString postfix = "-" + QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmss") + ".log"; m_tunnelsFile.rename(m_tunnelsFileName + postfix); qCDebug(dcApplication()) << "Rotate logfile" << m_tunnelsFile.fileName(); @@ -120,7 +120,7 @@ void LogEngine::rotateLogs() QString LogEngine::createTimestamp() { - return QString::number(QDateTime::currentDateTime().toTime_t()); + return QString::number(QDateTime::currentDateTimeUtc().toTime_t()); } void LogEngine::enable() diff --git a/libnymea-remoteproxy/logengine.h b/libnymea-remoteproxy/logengine.h index 73c4014..5acf8b3 100644 --- a/libnymea-remoteproxy/logengine.h +++ b/libnymea-remoteproxy/logengine.h @@ -31,7 +31,7 @@ #include #include -#include "tunnelconnection.h" +#include "proxy/tunnelconnection.h" namespace remoteproxy { diff --git a/libnymea-remoteproxy/loggingcategories.cpp b/libnymea-remoteproxy/loggingcategories.cpp index 5bf36b7..6276313 100644 --- a/libnymea-remoteproxy/loggingcategories.cpp +++ b/libnymea-remoteproxy/loggingcategories.cpp @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2020, nymea GmbH +* Copyright 2013 - 2021, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. @@ -39,6 +39,9 @@ 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(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 f9b4464..2c4addd 100644 --- a/libnymea-remoteproxy/loggingcategories.h +++ b/libnymea-remoteproxy/loggingcategories.h @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2020, nymea GmbH +* Copyright 2013 - 2021, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. @@ -43,6 +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(dcMonitorServer) Q_DECLARE_LOGGING_CATEGORY(dcAwsCredentialsProvider) diff --git a/libnymea-remoteproxy/proxyserver.cpp b/libnymea-remoteproxy/proxy/proxyserver.cpp similarity index 97% rename from libnymea-remoteproxy/proxyserver.cpp rename to libnymea-remoteproxy/proxy/proxyserver.cpp index 6048114..6e837f0 100644 --- a/libnymea-remoteproxy/proxyserver.cpp +++ b/libnymea-remoteproxy/proxy/proxyserver.cpp @@ -36,10 +36,13 @@ namespace remoteproxy { -ProxyServer::ProxyServer(QObject *parent) : QObject(parent) +ProxyServer::ProxyServer(QObject *parent) : + QObject(parent) { qRegisterMetaType("ProxyClient *"); m_jsonRpcServer = new JsonRpcServer(this); + m_jsonRpcServer->registerHandler(m_jsonRpcServer); + m_jsonRpcServer->registerHandler(new AuthenticationHandler(this)); loadStatistics(); } @@ -120,7 +123,7 @@ void ProxyServer::setRunning(bool running) if (m_running == running) return; - qCDebug(dcProxyServer()) << "The proxy server is now up and running"; + qCDebug(dcProxyServer()) << "The proxy server is" << (running ? "up and running." : "not running any more."); m_running = running; emit runningChanged(); } @@ -196,13 +199,13 @@ void ProxyServer::establishTunnel(ProxyClient *firstClient, ProxyClient *secondC Q_ARG(QString, m_jsonRpcServer->name()), Q_ARG(QString, "TunnelEstablished"), Q_ARG(QVariantMap, notificationParamsFirst), - Q_ARG(ProxyClient *, tunnel.clientOne())); + Q_ARG(ProxyClient*, tunnel.clientOne())); QMetaObject::invokeMethod(m_jsonRpcServer, QString("sendNotification").toLatin1().data(), Qt::QueuedConnection, Q_ARG(QString, m_jsonRpcServer->name()), Q_ARG(QString, "TunnelEstablished"), Q_ARG(QVariantMap, notificationParamsSecond), - Q_ARG(ProxyClient *, tunnel.clientTwo())); + Q_ARG(ProxyClient*, tunnel.clientTwo())); } void ProxyServer::onClientConnected(const QUuid &clientId, const QHostAddress &address) diff --git a/libnymea-remoteproxy/proxyserver.h b/libnymea-remoteproxy/proxy/proxyserver.h similarity index 97% rename from libnymea-remoteproxy/proxyserver.h rename to libnymea-remoteproxy/proxy/proxyserver.h index 952d3d7..edc5ba2 100644 --- a/libnymea-remoteproxy/proxyserver.h +++ b/libnymea-remoteproxy/proxy/proxyserver.h @@ -33,9 +33,9 @@ #include #include "proxyclient.h" -#include "jsonrpcserver.h" #include "tunnelconnection.h" -#include "transportinterface.h" +#include "server/jsonrpcserver.h" +#include "server/transportinterface.h" namespace remoteproxy { @@ -47,6 +47,8 @@ public: ~ProxyServer(); bool running() const; + void setRunning(bool running); + void registerTransportInterface(TransportInterface *interface); QVariantMap currentStatistics(); @@ -78,10 +80,6 @@ private: int m_totalTunnelCount = 0; int m_totalTraffic = 0; - - // Set private properties - void setRunning(bool running); - void loadStatistics(); void saveStatistics(); diff --git a/libnymea-remoteproxy/tunnelconnection.cpp b/libnymea-remoteproxy/proxy/tunnelconnection.cpp similarity index 97% rename from libnymea-remoteproxy/tunnelconnection.cpp rename to libnymea-remoteproxy/proxy/tunnelconnection.cpp index 21aba8b..d097d9e 100644 --- a/libnymea-remoteproxy/tunnelconnection.cpp +++ b/libnymea-remoteproxy/proxy/tunnelconnection.cpp @@ -35,7 +35,7 @@ TunnelConnection::TunnelConnection(ProxyClient *clientOne, ProxyClient *clientTw m_clientOne(clientOne), m_clientTwo(clientTwo) { - m_creationTimeStamp = QDateTime::currentDateTime().toTime_t(); + m_creationTimeStamp = QDateTime::currentDateTimeUtc().toTime_t(); } QString TunnelConnection::token() const diff --git a/libnymea-remoteproxy/tunnelconnection.h b/libnymea-remoteproxy/proxy/tunnelconnection.h similarity index 100% rename from libnymea-remoteproxy/tunnelconnection.h rename to libnymea-remoteproxy/proxy/tunnelconnection.h diff --git a/libnymea-remoteproxy/proxyclient.cpp b/libnymea-remoteproxy/proxyclient.cpp index 0fc0bfc..35487ae 100644 --- a/libnymea-remoteproxy/proxyclient.cpp +++ b/libnymea-remoteproxy/proxyclient.cpp @@ -39,7 +39,7 @@ ProxyClient::ProxyClient(TransportInterface *interface, const QUuid &clientId, c m_clientId(clientId), m_peerAddress(address) { - m_creationTimeStamp = QDateTime::currentDateTime().toTime_t(); + m_creationTimeStamp = QDateTime::currentDateTimeUtc().toTime_t(); m_timer = new QTimer(this); connect(m_timer, &QTimer::timeout, this, &ProxyClient::timeoutOccured); diff --git a/libnymea-remoteproxy/proxyclient.h b/libnymea-remoteproxy/proxyclient.h index 492a429..996a49b 100644 --- a/libnymea-remoteproxy/proxyclient.h +++ b/libnymea-remoteproxy/proxyclient.h @@ -34,7 +34,7 @@ #include #include -#include "transportinterface.h" +#include "server/transportinterface.h" namespace remoteproxy { diff --git a/libnymea-remoteproxy/proxyconfiguration.cpp b/libnymea-remoteproxy/proxyconfiguration.cpp index 5d88dc0..d876c21 100644 --- a/libnymea-remoteproxy/proxyconfiguration.cpp +++ b/libnymea-remoteproxy/proxyconfiguration.cpp @@ -302,24 +302,24 @@ QSslConfiguration ProxyConfiguration::sslConfiguration() const return m_sslConfiguration; } -QHostAddress ProxyConfiguration::webSocketServerHost() const +QHostAddress ProxyConfiguration::webSocketServerProxyHost() const { - return m_webSocketServerHost; + return m_webSocketServerProxyHost; } void ProxyConfiguration::setWebSocketServerHost(const QHostAddress &address) { - m_webSocketServerHost = address; + m_webSocketServerProxyHost = address; } -quint16 ProxyConfiguration::webSocketServerPort() const +quint16 ProxyConfiguration::webSocketServerProxyPort() const { - return m_webSocketServerPort; + return m_webSocketServerProxyPort; } void ProxyConfiguration::setWebSocketServerPort(quint16 port) { - m_webSocketServerPort = port; + m_webSocketServerProxyPort = port; } QHostAddress ProxyConfiguration::tcpServerHost() const @@ -342,6 +342,46 @@ void ProxyConfiguration::setTcpServerPort(quint16 port) m_tcpServerPort = port; } +QHostAddress ProxyConfiguration::webSocketServerTunnelProxyHost() const +{ + return m_webSocketServerTunnelProxyHost; +} + +void ProxyConfiguration::setWebSocketServerTunnelProxyHost(const QHostAddress &address) +{ + m_webSocketServerTunnelProxyHost = address; +} + +quint16 ProxyConfiguration::webSocketServerTunnelProxyPort() const +{ + return m_webSocketServerTunnelProxyPort; +} + +void ProxyConfiguration::setWebSocketServerTunnelProxyPort(quint16 port) +{ + m_webSocketServerTunnelProxyPort = port; +} + +QHostAddress ProxyConfiguration::tcpServerTunnelProxyHost() const +{ + return m_tcpServerTunnelProxyHost; +} + +void ProxyConfiguration::setTcpServerTunnelProxyHost(const QHostAddress &address) +{ + m_tcpServerTunnelProxyHost = address; +} + +quint16 ProxyConfiguration::tcpServerTunnelProxyPort() const +{ + return m_tcpServerTunnelProxyPort; +} + +void ProxyConfiguration::setTcpServerTunnelProxyPort(quint16 port) +{ + m_tcpServerTunnelProxyPort = port; +} + QDebug operator<<(QDebug debug, ProxyConfiguration *configuration) { debug.nospace() << endl << "========== ProxyConfiguration ==========" << endl; @@ -380,12 +420,18 @@ QDebug operator<<(QDebug debug, ProxyConfiguration *configuration) debug.nospace() << " Locality name:" << configuration->sslConfiguration().localCertificate().issuerInfo(QSslCertificate::LocalityName) << endl; debug.nospace() << " State/Province:" << configuration->sslConfiguration().localCertificate().issuerInfo(QSslCertificate::StateOrProvinceName) << endl; debug.nospace() << " Email address:" << configuration->sslConfiguration().localCertificate().issuerInfo(QSslCertificate::EmailAddress) << endl; - debug.nospace() << "WebSocketServer configuration" << endl; - debug.nospace() << " - Host:" << configuration->webSocketServerHost().toString() << endl; - debug.nospace() << " - Port:" << configuration->webSocketServerPort() << endl; - debug.nospace() << "TcpServer" << endl; + debug.nospace() << "WebSocketServer Proxy" << endl; + debug.nospace() << " - Host:" << configuration->webSocketServerProxyHost().toString() << endl; + debug.nospace() << " - Port:" << configuration->webSocketServerProxyPort() << endl; + debug.nospace() << "TcpServer Proxy" << endl; debug.nospace() << " - Host:" << configuration->tcpServerHost().toString() << endl; debug.nospace() << " - Port:" << configuration->tcpServerPort() << endl; + debug.nospace() << "WebSocketServer TunnelProxy" << endl; + debug.nospace() << " - Host:" << configuration->webSocketServerTunnelProxyHost().toString() << endl; + debug.nospace() << " - Port:" << configuration->webSocketServerTunnelProxyPort() << endl; + debug.nospace() << "TcpServer TunnelProxy" << endl; + debug.nospace() << " - Host:" << configuration->tcpServerTunnelProxyHost().toString() << endl; + debug.nospace() << " - Port:" << configuration->tcpServerTunnelProxyPort() << endl; debug.nospace() << "========== ProxyConfiguration =========="; return debug; } diff --git a/libnymea-remoteproxy/proxyconfiguration.h b/libnymea-remoteproxy/proxyconfiguration.h index 2262761..ee61d05 100644 --- a/libnymea-remoteproxy/proxyconfiguration.h +++ b/libnymea-remoteproxy/proxyconfiguration.h @@ -100,10 +100,10 @@ public: QSslConfiguration sslConfiguration() const; // WebSocketServer - QHostAddress webSocketServerHost() const; + QHostAddress webSocketServerProxyHost() const; void setWebSocketServerHost(const QHostAddress &address); - quint16 webSocketServerPort() const; + quint16 webSocketServerProxyPort() const; void setWebSocketServerPort(quint16 port); // TcpServer @@ -113,6 +113,21 @@ public: quint16 tcpServerPort() const; void setTcpServerPort(quint16 port); + // WebSocketServer (tunnel) + QHostAddress webSocketServerTunnelProxyHost() const; + void setWebSocketServerTunnelProxyHost(const QHostAddress &address); + + quint16 webSocketServerTunnelProxyPort() const; + void setWebSocketServerTunnelProxyPort(quint16 port); + + // TcpServer (tunnel) + QHostAddress tcpServerTunnelProxyHost() const; + void setTcpServerTunnelProxyHost(const QHostAddress &address); + + quint16 tcpServerTunnelProxyPort() const; + void setTcpServerTunnelProxyPort(quint16 port); + + private: // ProxyServer QString m_fileName; @@ -139,14 +154,23 @@ private: QString m_sslCertificateChainFileName; QSslConfiguration m_sslConfiguration; - // WebSocketServer - QHostAddress m_webSocketServerHost = QHostAddress::LocalHost; - quint16 m_webSocketServerPort = 1212; + // WebSocketServer (proxy) + QHostAddress m_webSocketServerProxyHost = QHostAddress::LocalHost; + quint16 m_webSocketServerProxyPort = 1212; - // TcpServer + // TcpServer (proxy) QHostAddress m_tcpServerHost = QHostAddress::LocalHost; quint16 m_tcpServerPort = 1213; + // WebSocketServer (proxy) + QHostAddress m_webSocketServerTunnelProxyHost = QHostAddress::LocalHost; + quint16 m_webSocketServerTunnelProxyPort = 2212; + + // TcpServer (proxy) + QHostAddress m_tcpServerTunnelProxyHost = QHostAddress::LocalHost; + quint16 m_tcpServerTunnelProxyPort = 2213; + + }; QDebug operator<< (QDebug debug, ProxyConfiguration *configuration); diff --git a/libnymea-remoteproxy/jsonrpcserver.cpp b/libnymea-remoteproxy/server/jsonrpcserver.cpp similarity index 98% rename from libnymea-remoteproxy/jsonrpcserver.cpp rename to libnymea-remoteproxy/server/jsonrpcserver.cpp index 6753967..2bbf9c5 100644 --- a/libnymea-remoteproxy/jsonrpcserver.cpp +++ b/libnymea-remoteproxy/server/jsonrpcserver.cpp @@ -70,7 +70,6 @@ JsonRpcServer::JsonRpcServer(QObject *parent) : params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String)); setParams("TunnelEstablished", params); - QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); } JsonRpcServer::~JsonRpcServer() @@ -252,12 +251,6 @@ void JsonRpcServer::processDataPackage(ProxyClient *proxyClient, const QByteArra } } -void JsonRpcServer::setup() -{ - registerHandler(this); - registerHandler(new AuthenticationHandler(this)); -} - void JsonRpcServer::asyncReplyFinished() { JsonReply *reply = static_cast(sender()); diff --git a/libnymea-remoteproxy/jsonrpcserver.h b/libnymea-remoteproxy/server/jsonrpcserver.h similarity index 99% rename from libnymea-remoteproxy/jsonrpcserver.h rename to libnymea-remoteproxy/server/jsonrpcserver.h index 19249da..e6f1aba 100644 --- a/libnymea-remoteproxy/jsonrpcserver.h +++ b/libnymea-remoteproxy/server/jsonrpcserver.h @@ -51,6 +51,9 @@ public: Q_INVOKABLE JsonReply *Hello(const QVariantMap ¶ms, ProxyClient *proxyClient = nullptr) const; Q_INVOKABLE JsonReply *Introspect(const QVariantMap ¶ms, ProxyClient *proxyClient = nullptr) const; + void registerHandler(JsonHandler *handler); + void unregisterHandler(JsonHandler *handler); + signals: void TunnelEstablished(const QVariantMap ¶ms); @@ -66,12 +69,9 @@ private: QString formatAssertion(const QString &targetNamespace, const QString &method, JsonHandler *handler, const QVariantMap &data) const; - void registerHandler(JsonHandler *handler); - void unregisterHandler(JsonHandler *handler); void processDataPackage(ProxyClient *proxyClient, const QByteArray &data); private slots: - void setup(); void asyncReplyFinished(); public slots: diff --git a/libnymea-remoteproxy/monitorserver.cpp b/libnymea-remoteproxy/server/monitorserver.cpp similarity index 100% rename from libnymea-remoteproxy/monitorserver.cpp rename to libnymea-remoteproxy/server/monitorserver.cpp diff --git a/libnymea-remoteproxy/monitorserver.h b/libnymea-remoteproxy/server/monitorserver.h similarity index 100% rename from libnymea-remoteproxy/monitorserver.h rename to libnymea-remoteproxy/server/monitorserver.h diff --git a/libnymea-remoteproxy/tcpsocketserver.cpp b/libnymea-remoteproxy/server/tcpsocketserver.cpp similarity index 75% rename from libnymea-remoteproxy/tcpsocketserver.cpp rename to libnymea-remoteproxy/server/tcpsocketserver.cpp index 7fd358d..af7ebbf 100644 --- a/libnymea-remoteproxy/tcpsocketserver.cpp +++ b/libnymea-remoteproxy/server/tcpsocketserver.cpp @@ -1,23 +1,29 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2019 Simon Stürz * - * * - * This file is part of nymea-remoteproxy. * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program 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 program. If not, see . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, 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 "tcpsocketserver.h" #include "loggingcategories.h" @@ -106,7 +112,7 @@ bool TcpSocketServer::startServer() } connect(m_server, &SslServer::clientConnected, this, &TcpSocketServer::onClientConnected); - connect(m_server, SIGNAL(clientDisconnected(QSslSocket *)), SLOT(onClientDisconnected(QSslSocket *))); + connect(m_server, SIGNAL(clientDisconnected(QSslSocket*)), SLOT(onClientDisconnected(QSslSocket*))); connect(m_server, &SslServer::dataAvailable, this, &TcpSocketServer::onDataAvailable); qCDebug(dcTcpSocketServer()) << "Server started successfully."; return true; @@ -144,7 +150,7 @@ void SslServer::incomingConnection(qintptr socketDescriptor) qCDebug(dcTcpSocketServer()) << "Incomming connection" << sslSocket; connect(sslSocket, &QSslSocket::readyRead, this, &SslServer::onSocketReadyRead); connect(sslSocket, &QSslSocket::disconnected, this, &SslServer::onClientDisconnected); - connect(sslSocket, &QSslSocket::encrypted, [this, sslSocket](){ + connect(sslSocket, &QSslSocket::encrypted, this, [this, sslSocket](){ qCDebug(dcTcpSocketServer()) << "SSL encryption established for" << sslSocket; emit clientConnected(sslSocket); }); diff --git a/libnymea-remoteproxy/tcpsocketserver.h b/libnymea-remoteproxy/server/tcpsocketserver.h similarity index 100% rename from libnymea-remoteproxy/tcpsocketserver.h rename to libnymea-remoteproxy/server/tcpsocketserver.h diff --git a/libnymea-remoteproxy/transportinterface.cpp b/libnymea-remoteproxy/server/transportinterface.cpp similarity index 100% rename from libnymea-remoteproxy/transportinterface.cpp rename to libnymea-remoteproxy/server/transportinterface.cpp diff --git a/libnymea-remoteproxy/transportinterface.h b/libnymea-remoteproxy/server/transportinterface.h similarity index 100% rename from libnymea-remoteproxy/transportinterface.h rename to libnymea-remoteproxy/server/transportinterface.h diff --git a/libnymea-remoteproxy/websocketserver.cpp b/libnymea-remoteproxy/server/websocketserver.cpp similarity index 100% rename from libnymea-remoteproxy/websocketserver.cpp rename to libnymea-remoteproxy/server/websocketserver.cpp diff --git a/libnymea-remoteproxy/websocketserver.h b/libnymea-remoteproxy/server/websocketserver.h similarity index 100% rename from libnymea-remoteproxy/websocketserver.h rename to libnymea-remoteproxy/server/websocketserver.h diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.cpp new file mode 100644 index 0000000..5e692a7 --- /dev/null +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.cpp @@ -0,0 +1,141 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 + + + TunnelProxyServer *proxyServer = new TunnelProxyServer(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 manager..."; + foreach (TransportInterface *interface, m_transportInterfaces) { + interface->startServer(); + } + setRunning(true); +} + +void TunnelProxyManager::stopServer() +{ + qCDebug(dcTunnelProxyManager()) << "Stopping tunnel proxy server..."; + 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) +{ + Q_UNUSED(clientId) +} + +void TunnelProxyManager::onClientDataAvailable(const QUuid &clientId, const QByteArray &data) +{ + Q_UNUSED(clientId) + Q_UNUSED(data) +} + +} diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.h b/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.h new file mode 100644 index 0000000..985bec1 --- /dev/null +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxymanager.h @@ -0,0 +1,93 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef TUNNELPROXYMANAGER_H +#define TUNNELPROXYMANAGER_H + +#include + +#include "server/jsonrpcserver.h" +#include "server/transportinterface.h" + +#include "tunnelproxyserver.h" + +namespace remoteproxy { + +class TunnelProxyManager : public QObject +{ + Q_OBJECT +public: + enum Error { + ErrorNoError, + ErrorServerNotFound + }; + Q_ENUM(Error) + + explicit TunnelProxyManager(QObject *parent = nullptr); + ~TunnelProxyManager(); + + bool running() const; + void setRunning(bool running); + + void registerTransportInterface(TransportInterface *interface); + + TunnelProxyManager::Error registerServer(const QUuid &clientId, const QUuid &serverUuid, const QString &serverName); + +public slots: + void startServer(); + void stopServer(); + + void tick(); + +signals: + void runningChanged(bool running); + +private slots: + void onClientConnected(const QUuid &clientId, const QHostAddress &address); + 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 + + // Server connections + QHash m_proxyClientsTunnelServer; // clientUuid, object + QHash m_tunnelServers; // server uuid, object + +}; + +} + +#endif // TUNNELPROXYMANAGER_H diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp new file mode 100644 index 0000000..b6c8153 --- /dev/null +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp @@ -0,0 +1,56 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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" + +namespace remoteproxy { + +TunnelProxyServer::TunnelProxyServer(ProxyClient *proxyClient, const QUuid &serverUuid, const QString &serverName, QObject *parent) : + QObject(parent), + m_proxyClient(proxyClient), + m_serverUuid(serverUuid), + m_serverName(serverName) +{ + +} + +ProxyClient *TunnelProxyServer::proxyClient() const +{ + return m_proxyClient; +} + +QUuid TunnelProxyServer::serverUuid() const +{ + return m_serverUuid; +} + +QString TunnelProxyServer::serverName() const +{ + return m_serverName; +} + +} diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.h b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.h new file mode 100644 index 0000000..5fd9d10 --- /dev/null +++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.h @@ -0,0 +1,59 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef TUNNELPROXYSERVER_H +#define TUNNELPROXYSERVER_H + +#include + +#include "proxyclient.h" + +namespace remoteproxy { + +class TunnelProxyServer : public QObject +{ + Q_OBJECT +public: + explicit TunnelProxyServer(ProxyClient *proxyClient, const QUuid &serverUuid, const QString &serverName, QObject *parent = nullptr); + + ProxyClient *proxyClient() const; + + QUuid serverUuid() const; + QString serverName() const; + +signals: + +private: + ProxyClient *m_proxyClient = nullptr; + + QUuid m_serverUuid; + QString m_serverName; +}; + +} + +#endif // TUNNELPROXYSERVER_H diff --git a/monitor/main.cpp b/monitor/main.cpp index ec76991..87bf742 100644 --- a/monitor/main.cpp +++ b/monitor/main.cpp @@ -48,7 +48,7 @@ int main(int argc, char *argv[]) parser.setApplicationDescription(QString("\nThe nymea remote proxy monitor allowes to monitor the live server activity on the a local instance.\n\n" "Server version: %1\n" "API version: %2\n\n" - "Copyright %3 2018 Simon Stürz \n") + "Copyright %3 2021 nymea GmbH \n") .arg(SERVER_VERSION_STRING) .arg(API_VERSION_STRING) .arg(QChar(0xA9))); diff --git a/monitor/terminalwindow.cpp b/monitor/terminalwindow.cpp index 8842154..0942f21 100644 --- a/monitor/terminalwindow.cpp +++ b/monitor/terminalwindow.cpp @@ -101,7 +101,7 @@ const char *TerminalWindow::convertString(const QString &string) QString TerminalWindow::getDurationString(uint timestamp) { - uint duration = QDateTime::currentDateTime().toTime_t() - timestamp; + uint duration = QDateTime::currentDateTimeUtc().toTime_t() - timestamp; int seconds = static_cast(duration % 60); duration /= 60; int minutes = static_cast(duration % 60); diff --git a/server/main.cpp b/server/main.cpp index 1e95df4..dd6258b 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -65,7 +65,7 @@ static const char *const error = "\e[31m"; static void consoleLogHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { QString messageString; - QString timeString = QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz"); + QString timeString = QDateTime::currentDateTimeUtc().toString("yyyy.MM.dd hh:mm:ss.zzz"); switch (type) { case QtInfoMsg: messageString = QString(" I %1 | %2: %3").arg(timeString).arg(context.category).arg(message); @@ -174,7 +174,7 @@ int main(int argc, char *argv[]) } // Verify webserver configuration - if (configuration->webSocketServerHost().isNull()) { + if (configuration->webSocketServerProxyHost().isNull()) { qCCritical(dcApplication()) << "Invalid web socket host address passed."; exit(-1); } diff --git a/tests/test-offline/nymea-remoteproxy-tests-offline.cpp b/tests/test-proxy/remoteproxytestsproxy.cpp similarity index 91% rename from tests/test-offline/nymea-remoteproxy-tests-offline.cpp rename to tests/test-proxy/remoteproxytestsproxy.cpp index 8d373ab..d9fddd0 100644 --- a/tests/test-offline/nymea-remoteproxy-tests-offline.cpp +++ b/tests/test-proxy/remoteproxytestsproxy.cpp @@ -25,7 +25,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include "nymea-remoteproxy-tests-offline.h" +#include "remoteproxytestsproxy.h" #include "engine.h" #include "loggingcategories.h" @@ -37,19 +37,19 @@ #include #include -RemoteProxyOfflineTests::RemoteProxyOfflineTests(QObject *parent) : +RemoteProxyTestsProxy::RemoteProxyTestsProxy(QObject *parent) : BaseTest(parent) { } -void RemoteProxyOfflineTests::startStopServer() +void RemoteProxyTestsProxy::startStopServer() { startServer(); stopServer(); } -void RemoteProxyOfflineTests::dummyAuthenticator() +void RemoteProxyTestsProxy::dummyAuthenticator() { cleanUpEngine(); @@ -69,7 +69,7 @@ void RemoteProxyOfflineTests::dummyAuthenticator() // Make sure the server is running QVERIFY(Engine::instance()->running()); - QVERIFY(Engine::instance()->webSocketServer()->running()); + QVERIFY(Engine::instance()->webSocketServerProxy()->running()); QVERIFY(Engine::instance()->proxyServer()->running()); // Create request @@ -78,7 +78,7 @@ void RemoteProxyOfflineTests::dummyAuthenticator() params.insert("name", "test"); params.insert("token", "foobar"); - QVariant response = invokeWebSocketApiCall("Authentication.Authenticate", params); + QVariant response = invokeWebSocketProxyApiCall("Authentication.Authenticate", params); qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); verifyAuthenticationError(response); @@ -86,7 +86,7 @@ void RemoteProxyOfflineTests::dummyAuthenticator() } -void RemoteProxyOfflineTests::monitorServer() +void RemoteProxyTestsProxy::monitorServer() { // Start the server startServer(); @@ -188,7 +188,7 @@ void RemoteProxyOfflineTests::monitorServer() stopServer(); } -void RemoteProxyOfflineTests::configuration_data() +void RemoteProxyTestsProxy::configuration_data() { QTest::addColumn("fileName"); QTest::addColumn("success"); @@ -201,7 +201,7 @@ void RemoteProxyOfflineTests::configuration_data() QTest::newRow("faulty chain") << ":/test-configuration-faulty-chain.conf" << false; } -void RemoteProxyOfflineTests::configuration() +void RemoteProxyTestsProxy::configuration() { QFETCH(QString, fileName); QFETCH(bool, success); @@ -210,7 +210,7 @@ void RemoteProxyOfflineTests::configuration() QCOMPARE(configuration.loadConfiguration(fileName), success); } -void RemoteProxyOfflineTests::serverPortBlocked() +void RemoteProxyTestsProxy::serverPortBlocked() { cleanUpEngine(); @@ -222,7 +222,7 @@ void RemoteProxyOfflineTests::serverPortBlocked() // Create a dummy server which blocks the port QWebSocketServer dummyServer("dummy-server", QWebSocketServer::NonSecureMode); - QVERIFY(dummyServer.listen(QHostAddress::LocalHost, m_configuration->webSocketServerPort())); + QVERIFY(dummyServer.listen(QHostAddress::LocalHost, m_configuration->webSocketServerProxyPort())); // Start proxy webserver QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged); @@ -235,7 +235,7 @@ void RemoteProxyOfflineTests::serverPortBlocked() QVERIFY(Engine::instance()->running()); // Make sure the websocket server is not running - QVERIFY(!Engine::instance()->webSocketServer()->running()); + QVERIFY(!Engine::instance()->webSocketServerProxy()->running()); QSignalSpy closedSpy(&dummyServer, &QWebSocketServer::closed); dummyServer.close(); @@ -272,7 +272,7 @@ void RemoteProxyOfflineTests::serverPortBlocked() QVERIFY(Engine::instance()->running()); // Make sure the TCP server is not running - QVERIFY(!Engine::instance()->tcpSocketServer()->running()); + QVERIFY(!Engine::instance()->tcpSocketServerProxy()->running()); tcpDummyServer->close(); delete tcpDummyServer; @@ -281,14 +281,14 @@ void RemoteProxyOfflineTests::serverPortBlocked() startServer(); // Make sure the TCP server is not running - QVERIFY(Engine::instance()->webSocketServer()->running()); - QVERIFY(Engine::instance()->tcpSocketServer()->running()); + QVERIFY(Engine::instance()->webSocketServerProxy()->running()); + QVERIFY(Engine::instance()->tcpSocketServerProxy()->running()); // Clean up stopServer(); } -void RemoteProxyOfflineTests::websocketBinaryData() +void RemoteProxyTestsProxy::websocketBinaryData() { // Start the server startServer(); @@ -296,7 +296,7 @@ void RemoteProxyOfflineTests::websocketBinaryData() QWebSocket *client = new QWebSocket("bad-client", QWebSocketProtocol::Version13); connect(client, &QWebSocket::sslErrors, this, &BaseTest::sslErrors); QSignalSpy spyConnection(client, SIGNAL(connected())); - client->open(Engine::instance()->webSocketServer()->serverUrl()); + client->open(Engine::instance()->webSocketServerProxy()->serverUrl()); spyConnection.wait(); // Send binary data and make sure the server disconnects this socket @@ -309,7 +309,7 @@ void RemoteProxyOfflineTests::websocketBinaryData() stopServer(); } -void RemoteProxyOfflineTests::websocketPing() +void RemoteProxyTestsProxy::websocketPing() { // Start the server startServer(); @@ -317,7 +317,7 @@ void RemoteProxyOfflineTests::websocketPing() QWebSocket *client = new QWebSocket("bad-client", QWebSocketProtocol::Version13); connect(client, &QWebSocket::sslErrors, this, &BaseTest::sslErrors); QSignalSpy spyConnection(client, SIGNAL(connected())); - client->open(Engine::instance()->webSocketServer()->serverUrl()); + client->open(Engine::instance()->webSocketServerProxy()->serverUrl()); spyConnection.wait(); QVERIFY(spyConnection.count() == 1); @@ -334,45 +334,45 @@ void RemoteProxyOfflineTests::websocketPing() stopServer(); } -//void RemoteProxyOfflineTests::apiBasicCallsTcp_data() -//{ -// QTest::addColumn("data"); -// QTest::addColumn("responseId"); -// QTest::addColumn("responseStatus"); +void RemoteProxyTestsProxy::apiBasicCallsTcp_data() +{ + QTest::addColumn("data"); + QTest::addColumn("responseId"); + QTest::addColumn("responseStatus"); -// QTest::newRow("valid call") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\"}") << 42 << "success"; -// QTest::newRow("missing id") << QByteArray("{\"method\":\"RemoteProxy.Hello\"}") << -1 << "error"; -// QTest::newRow("missing method") << QByteArray("{\"id\":42}") << 42 << "error"; -// QTest::newRow("invalid json") << QByteArray("{\"id\":42, \"method\":\"RemoteProx") << -1 << "error"; -// QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Explode\"}") << 42 << "error"; -// QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"ProxyRemote.Hello\"}") << 42 << "error"; -// QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"RemoteProxyHello\"}") << 42 << "error"; -// QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\", \"params\":{\"törööö\":\"chooo-chooo\"}}") << 42 << "error"; -// QTest::newRow("invalid authentication params") << QByteArray("{\"id\":42, \"method\":\"Authentication.Authenticate\", \"params\":{\"your\":\"mamma\"}}") << 42 << "error"; -//} + QTest::newRow("valid call") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\"}") << 42 << "success"; + QTest::newRow("missing id") << QByteArray("{\"method\":\"RemoteProxy.Hello\"}") << -1 << "error"; + QTest::newRow("missing method") << QByteArray("{\"id\":42}") << 42 << "error"; + //QTest::newRow("invalid json") << QByteArray("{\"id\":42, \"method\":\"RemoteProx") << -1 << "error"; + QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Explode\"}") << 42 << "error"; + QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"ProxyRemote.Hello\"}") << 42 << "error"; + QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"RemoteProxyHello\"}") << 42 << "error"; + QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\", \"params\":{\"törööö\":\"chooo-chooo\"}}") << 42 << "error"; + QTest::newRow("invalid authentication params") << QByteArray("{\"id\":42, \"method\":\"Authentication.Authenticate\", \"params\":{\"your\":\"mamma\"}}") << 42 << "error"; +} -//void RemoteProxyOfflineTests::apiBasicCallsTcp() -//{ -// QFETCH(QByteArray, data); -// QFETCH(int, responseId); -// QFETCH(QString, responseStatus); +void RemoteProxyTestsProxy::apiBasicCallsTcp() +{ + QFETCH(QByteArray, data); + QFETCH(int, responseId); + QFETCH(QString, responseStatus); -// // Start the server -// startServer(); + // Start the server + startServer(); -// QVariant response = injectTcpSocketData(data); -// QVERIFY(!response.isNull()); + QVariant response = injectTcpSocketProxyData(data); + QVERIFY(!response.isNull()); -// qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); + qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); -// QCOMPARE(response.toMap().value("id").toInt(), responseId); -// QCOMPARE(response.toMap().value("status").toString(), responseStatus); + QCOMPARE(response.toMap().value("id").toInt(), responseId); + QCOMPARE(response.toMap().value("status").toString(), responseStatus); -// // Clean up -// stopServer(); -//} + // Clean up + stopServer(); +} -void RemoteProxyOfflineTests::getIntrospect() +void RemoteProxyTestsProxy::getIntrospect() { // Start the server startServer(); @@ -380,7 +380,7 @@ void RemoteProxyOfflineTests::getIntrospect() QVariantMap response; // WebSocket - response = invokeWebSocketApiCall("RemoteProxy.Introspect").toMap(); + response = invokeWebSocketProxyApiCall("RemoteProxy.Introspect").toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); @@ -390,7 +390,7 @@ void RemoteProxyOfflineTests::getIntrospect() // Tcp response.clear(); - response = invokeTcpSocketApiCall("RemoteProxy.Introspect").toMap(); + response = invokeTcpSocketProxyApiCall("RemoteProxy.Introspect").toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); QVERIFY(!response.isEmpty()); @@ -403,14 +403,14 @@ void RemoteProxyOfflineTests::getIntrospect() stopServer(); } -void RemoteProxyOfflineTests::getHello() +void RemoteProxyTestsProxy::getHello() { // Start the server startServer(); QVariantMap response; // WebSocket - response = invokeWebSocketApiCall("RemoteProxy.Hello").toMap(); + response = invokeWebSocketProxyApiCall("RemoteProxy.Hello").toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); // Verify data @@ -422,7 +422,7 @@ void RemoteProxyOfflineTests::getHello() // TCP response.clear(); - response = invokeTcpSocketApiCall("RemoteProxy.Hello").toMap(); + response = invokeTcpSocketProxyApiCall("RemoteProxy.Hello").toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); // Verify data @@ -437,7 +437,7 @@ void RemoteProxyOfflineTests::getHello() stopServer(); } -void RemoteProxyOfflineTests::apiBasicCalls_data() +void RemoteProxyTestsProxy::apiBasicCalls_data() { QTest::addColumn("data"); QTest::addColumn("responseId"); @@ -454,7 +454,7 @@ void RemoteProxyOfflineTests::apiBasicCalls_data() QTest::newRow("invalid authentication params") << QByteArray("{\"id\":42, \"method\":\"Authentication.Authenticate\", \"params\":{\"your\":\"mamma\"}}") << 42 << "error"; } -void RemoteProxyOfflineTests::apiBasicCalls() +void RemoteProxyTestsProxy::apiBasicCalls() { QFETCH(QByteArray, data); QFETCH(int, responseId); @@ -466,7 +466,7 @@ void RemoteProxyOfflineTests::apiBasicCalls() QVariantMap response; // Websocket - response = injectWebSocketData(data).toMap(); + response = injectWebSocketProxyData(data).toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); QVERIFY(!response.isEmpty()); QCOMPARE(response.value("id").toInt(), responseId); @@ -474,7 +474,7 @@ void RemoteProxyOfflineTests::apiBasicCalls() // TCP response.clear(); - response = injectTcpSocketData(data).toMap(); + response = injectTcpSocketProxyData(data).toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); QVERIFY(!response.isEmpty()); QCOMPARE(response.value("id").toInt(), responseId); @@ -484,7 +484,7 @@ void RemoteProxyOfflineTests::apiBasicCalls() stopServer(); } -void RemoteProxyOfflineTests::authenticate_data() +void RemoteProxyTestsProxy::authenticate_data() { QTest::addColumn("uuid"); QTest::addColumn("name"); @@ -518,7 +518,7 @@ void RemoteProxyOfflineTests::authenticate_data() } -void RemoteProxyOfflineTests::authenticate() +void RemoteProxyTestsProxy::authenticate() { QFETCH(QString, uuid); QFETCH(QString, name); @@ -543,19 +543,19 @@ void RemoteProxyOfflineTests::authenticate() // WebSocket QVariantMap response; - response = invokeWebSocketApiCall("Authentication.Authenticate", params).toMap(); + response = invokeWebSocketProxyApiCall("Authentication.Authenticate", params).toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); verifyAuthenticationError(response, expectedError); // TCP - response = invokeTcpSocketApiCall("Authentication.Authenticate", params).toMap(); + response = invokeTcpSocketProxyApiCall("Authentication.Authenticate", params).toMap(); verifyAuthenticationError(response, expectedError); // Clean up stopServer(); } -void RemoteProxyOfflineTests::authenticateNonce() +void RemoteProxyTestsProxy::authenticateNonce() { // Start the server startServer(); @@ -651,7 +651,7 @@ void RemoteProxyOfflineTests::authenticateNonce() stopServer(); } -void RemoteProxyOfflineTests::authenticateSendData() +void RemoteProxyTestsProxy::authenticateSendData() { // Start the server startServer(); @@ -675,7 +675,7 @@ void RemoteProxyOfflineTests::authenticateSendData() QWebSocket *socket = new QWebSocket("proxy-testclient", QWebSocketProtocol::Version13); connect(socket, &QWebSocket::sslErrors, this, &BaseTest::sslErrors); QSignalSpy spyConnection(socket, &QWebSocket::connected); - socket->open(Engine::instance()->webSocketServer()->serverUrl()); + socket->open(Engine::instance()->webSocketServerProxy()->serverUrl()); spyConnection.wait(); QVERIFY(spyConnection.count() == 1); @@ -697,7 +697,7 @@ void RemoteProxyOfflineTests::authenticateSendData() stopServer(); } -void RemoteProxyOfflineTests::clientConnectionWebSocket() +void RemoteProxyTestsProxy::clientConnectionWebSocket() { // Start the server startServer(); @@ -747,7 +747,7 @@ void RemoteProxyOfflineTests::clientConnectionWebSocket() stopServer(); } -void RemoteProxyOfflineTests::clientConnectionTcpSocket() +void RemoteProxyTestsProxy::clientConnectionTcpSocket() { // Start the server startServer(); @@ -797,7 +797,7 @@ void RemoteProxyOfflineTests::clientConnectionTcpSocket() stopServer(); } -void RemoteProxyOfflineTests::remoteConnection() +void RemoteProxyTestsProxy::remoteConnection() { // Start the server startServer(); @@ -890,7 +890,7 @@ void RemoteProxyOfflineTests::remoteConnection() stopServer(); } -void RemoteProxyOfflineTests::multipleRemoteConnection() +void RemoteProxyTestsProxy::multipleRemoteConnection() { // Start the server startServer(); @@ -920,7 +920,7 @@ void RemoteProxyOfflineTests::multipleRemoteConnection() stopServer(); } -void RemoteProxyOfflineTests::trippleConnection() +void RemoteProxyTestsProxy::trippleConnection() { // Start the server startServer(); @@ -1005,7 +1005,7 @@ void RemoteProxyOfflineTests::trippleConnection() stopServer(); } -void RemoteProxyOfflineTests::duplicateUuid() +void RemoteProxyTestsProxy::duplicateUuid() { // Start the server startServer(); @@ -1067,7 +1067,7 @@ void RemoteProxyOfflineTests::duplicateUuid() stopServer(); } -void RemoteProxyOfflineTests::sslConfigurations() +void RemoteProxyTestsProxy::sslConfigurations() { // Start the server startServer(); @@ -1098,7 +1098,7 @@ void RemoteProxyOfflineTests::sslConfigurations() stopServer(); } -void RemoteProxyOfflineTests::jsonRpcTimeout() +void RemoteProxyTestsProxy::jsonRpcTimeout() { // Start the server startServer(); @@ -1120,7 +1120,7 @@ void RemoteProxyOfflineTests::jsonRpcTimeout() params.insert("name", "name"); params.insert("token", "token"); - QVariant response = invokeWebSocketApiCall("Authentication.Authenticate", params); + QVariant response = invokeWebSocketProxyApiCall("Authentication.Authenticate", params); qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); QVERIFY(response.toMap().value("status").toString() == "error"); @@ -1129,7 +1129,7 @@ void RemoteProxyOfflineTests::jsonRpcTimeout() stopServer(); } -void RemoteProxyOfflineTests::inactiveTimeout() +void RemoteProxyTestsProxy::inactiveTimeout() { // Start the server startServer(); @@ -1152,7 +1152,7 @@ void RemoteProxyOfflineTests::inactiveTimeout() stopServer(); } -void RemoteProxyOfflineTests::authenticationReplyTimeout() +void RemoteProxyTestsProxy::authenticationReplyTimeout() { // Start the server startServer(); @@ -1171,7 +1171,7 @@ void RemoteProxyOfflineTests::authenticationReplyTimeout() params.insert("name", "Sleepy test client"); params.insert("token", "sleepy token zzzZZZ"); - QVariant response = invokeWebSocketApiCall("Authentication.Authenticate", params); + QVariant response = invokeWebSocketProxyApiCall("Authentication.Authenticate", params); qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); verifyAuthenticationError(response, Authenticator::AuthenticationErrorTimeout); @@ -1179,7 +1179,7 @@ void RemoteProxyOfflineTests::authenticationReplyTimeout() stopServer(); } -void RemoteProxyOfflineTests::authenticationReplyConnection() +void RemoteProxyTestsProxy::authenticationReplyConnection() { // Start the server startServer(); @@ -1212,7 +1212,7 @@ void RemoteProxyOfflineTests::authenticationReplyConnection() stopServer(); } -void RemoteProxyOfflineTests::tcpRemoteConnection() +void RemoteProxyTestsProxy::tcpRemoteConnection() { // Start the server startServer(); @@ -1306,7 +1306,7 @@ void RemoteProxyOfflineTests::tcpRemoteConnection() stopServer(); } -void RemoteProxyOfflineTests::tcpWebsocketRemoteConnection() +void RemoteProxyTestsProxy::tcpWebsocketRemoteConnection() { // Start the server startServer(); @@ -1400,4 +1400,4 @@ void RemoteProxyOfflineTests::tcpWebsocketRemoteConnection() stopServer(); } -QTEST_MAIN(RemoteProxyOfflineTests) +QTEST_MAIN(RemoteProxyTestsProxy) diff --git a/tests/test-offline/nymea-remoteproxy-tests-offline.h b/tests/test-proxy/remoteproxytestsproxy.h similarity index 89% rename from tests/test-offline/nymea-remoteproxy-tests-offline.h rename to tests/test-proxy/remoteproxytestsproxy.h index dc0a233..43c120f 100644 --- a/tests/test-offline/nymea-remoteproxy-tests-offline.h +++ b/tests/test-proxy/remoteproxytestsproxy.h @@ -25,20 +25,20 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef NYMEA_REMOTEPROXY_TESTS_OFFLINE_H -#define NYMEA_REMOTEPROXY_TESTS_OFFLINE_H +#ifndef REMOTEPROXYTESTSPROXY_H +#define REMOTEPROXYTESTSPROXY_H #include "basetest.h" using namespace remoteproxy; using namespace remoteproxyclient; -class RemoteProxyOfflineTests : public BaseTest +class RemoteProxyTestsProxy : public BaseTest { Q_OBJECT public: - explicit RemoteProxyOfflineTests(QObject *parent = nullptr); - ~RemoteProxyOfflineTests() = default; + explicit RemoteProxyTestsProxy(QObject *parent = nullptr); + ~RemoteProxyTestsProxy() = default; private slots: // Basic stuff @@ -61,6 +61,9 @@ private slots: void apiBasicCalls_data(); void apiBasicCalls(); + void apiBasicCallsTcp_data(); + void apiBasicCallsTcp(); + void authenticate_data(); void authenticate(); @@ -87,4 +90,4 @@ private slots: }; -#endif // NYMEA_REMOTEPROXY_TESTS_OFFLINE_H +#endif // REMOTEPROXYTESTSPROXY_H diff --git a/tests/test-offline/test-offline.pro b/tests/test-proxy/test-proxy.pro similarity index 55% rename from tests/test-offline/test-offline.pro rename to tests/test-proxy/test-proxy.pro index c6db854..dd5a788 100644 --- a/tests/test-offline/test-offline.pro +++ b/tests/test-proxy/test-proxy.pro @@ -4,11 +4,11 @@ include(../testbase/testbase.pri) CONFIG += testcase QT += testlib -TARGET = nymea-remoteproxy-tests-offline +TARGET = nymea-remoteproxy-proxy-tests -HEADERS += nymea-remoteproxy-tests-offline.h +HEADERS += remoteproxytestsproxy.h -SOURCES += nymea-remoteproxy-tests-offline.cpp +SOURCES += remoteproxytestsproxy.cpp target.path = $$[QT_INSTALL_PREFIX]/bin INSTALLS += target diff --git a/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp new file mode 100644 index 0000000..990daef --- /dev/null +++ b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp @@ -0,0 +1,202 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 "remoteproxyteststunnelproxy.h" + +#include "engine.h" +#include "loggingcategories.h" +#include "remoteproxyconnection.h" + +#include +#include +#include +#include +#include + +RemoteProxyTestsTunnelProxy::RemoteProxyTestsTunnelProxy(QObject *parent) : + BaseTest(parent) +{ + +} + +void RemoteProxyTestsTunnelProxy::startStopServer() +{ + startServer(); + stopServer(); +} + +void RemoteProxyTestsTunnelProxy::apiBasicCallsTcp_data() +{ + QTest::addColumn("data"); + QTest::addColumn("responseId"); + QTest::addColumn("responseStatus"); + + QTest::newRow("valid call") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\"}") << 42 << "success"; + QTest::newRow("missing id") << QByteArray("{\"method\":\"RemoteProxy.Hello\"}") << -1 << "error"; + QTest::newRow("missing method") << QByteArray("{\"id\":42}") << 42 << "error"; + //QTest::newRow("invalid json") << QByteArray("{\"id\":42, \"method\":\"RemoteProx") << -1 << "error"; + QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Explode\"}") << 42 << "error"; + QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"ProxyRemote.Hello\"}") << 42 << "error"; + QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"RemoteProxyHello\"}") << 42 << "error"; + QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\", \"params\":{\"törööö\":\"chooo-chooo\"}}") << 42 << "error"; + QTest::newRow("invalid authentication params") << QByteArray("{\"id\":42, \"method\":\"Authentication.Authenticate\", \"params\":{\"your\":\"mamma\"}}") << 42 << "error"; +} + +void RemoteProxyTestsTunnelProxy::apiBasicCallsTcp() +{ + QFETCH(QByteArray, data); + QFETCH(int, responseId); + QFETCH(QString, responseStatus); + + // Start the server + startServer(); + + QVariant response = injectTcpSocketProxyData(data); + QVERIFY(!response.isNull()); + + qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); + + QCOMPARE(response.toMap().value("id").toInt(), responseId); + QCOMPARE(response.toMap().value("status").toString(), responseStatus); + + // Clean up + stopServer(); +} + +void RemoteProxyTestsTunnelProxy::getIntrospect() +{ + // Start the server + startServer(); + + QVariantMap response; + + // WebSocket + response = invokeWebSocketTunnelProxyApiCall("RemoteProxy.Introspect").toMap(); + //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); + QVERIFY(!response.isEmpty()); + QVERIFY(response.value("status").toString() == "success"); + QVERIFY(response.value("params").toMap().contains("methods")); + QVERIFY(response.value("params").toMap().contains("notifications")); + QVERIFY(response.value("params").toMap().contains("types")); + + // Tcp + response.clear(); + response = invokeTcpSocketTunnelProxyApiCall("RemoteProxy.Introspect").toMap(); + //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); + + QVERIFY(!response.isEmpty()); + QVERIFY(response.value("status").toString() == "success"); + QVERIFY(response.value("params").toMap().contains("methods")); + QVERIFY(response.value("params").toMap().contains("notifications")); + QVERIFY(response.value("params").toMap().contains("types")); + + // Clean up + stopServer(); +} + +void RemoteProxyTestsTunnelProxy::getHello() +{ + // Start the server + startServer(); + QVariantMap response; + + // WebSocket + response = invokeWebSocketTunnelProxyApiCall("RemoteProxy.Hello").toMap(); + qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); + + // Verify data + QVERIFY(!response.isEmpty()); + QCOMPARE(response.value("params").toMap().value("name").toString(), Engine::instance()->configuration()->serverName()); + QCOMPARE(response.value("params").toMap().value("server").toString(), QString(SERVER_NAME_STRING)); + QCOMPARE(response.value("params").toMap().value("version").toString(), QString(SERVER_VERSION_STRING)); + QCOMPARE(response.value("params").toMap().value("apiVersion").toString(), QString(API_VERSION_STRING)); + + // TCP + response.clear(); + response = invokeTcpSocketTunnelProxyApiCall("RemoteProxy.Hello").toMap(); + //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); + + // Verify data + QVERIFY(!response.isEmpty()); + QCOMPARE(response.value("params").toMap().value("name").toString(), Engine::instance()->configuration()->serverName()); + QCOMPARE(response.value("params").toMap().value("server").toString(), QString(SERVER_NAME_STRING)); + QCOMPARE(response.value("params").toMap().value("version").toString(), QString(SERVER_VERSION_STRING)); + QCOMPARE(response.value("params").toMap().value("apiVersion").toString(), QString(API_VERSION_STRING)); + + + // Clean up + stopServer(); +} + +void RemoteProxyTestsTunnelProxy::apiBasicCalls_data() +{ + QTest::addColumn("data"); + QTest::addColumn("responseId"); + QTest::addColumn("responseStatus"); + + QTest::newRow("valid call") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\"}") << 42 << "success"; + QTest::newRow("missing id") << QByteArray("{\"method\":\"RemoteProxy.Hello\"}") << -1 << "error"; + QTest::newRow("missing method") << QByteArray("{\"id\":42}") << 42 << "error"; + QTest::newRow("invalid json") << QByteArray("{\"id\":42, \"method\":\"RemoteProx}") << -1 << "error"; + QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Explode\"}") << 42 << "error"; + QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"ProxyRemote.Hello\"}") << 42 << "error"; + QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"RemoteProxyHello\"}") << 42 << "error"; + QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\", \"params\":{\"törööö\":\"chooo-chooo\"}}") << 42 << "error"; + QTest::newRow("invalid authentication params") << QByteArray("{\"id\":42, \"method\":\"Authentication.Authenticate\", \"params\":{\"your\":\"mamma\"}}") << 42 << "error"; +} + +void RemoteProxyTestsTunnelProxy::apiBasicCalls() +{ + QFETCH(QByteArray, data); + QFETCH(int, responseId); + QFETCH(QString, responseStatus); + + // Start the server + startServer(); + + QVariantMap response; + + // Websocket + response = injectWebSocketTunnelProxyData(data).toMap(); + //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); + QVERIFY(!response.isEmpty()); + QCOMPARE(response.value("id").toInt(), responseId); + QCOMPARE(response.value("status").toString(), responseStatus); + + // TCP + response.clear(); + response = injectTcpSocketTunnelProxyData(data).toMap(); + //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); + QVERIFY(!response.isEmpty()); + QCOMPARE(response.value("id").toInt(), responseId); + QCOMPARE(response.value("status").toString(), responseStatus); + + // Clean up + stopServer(); +} + +QTEST_MAIN(RemoteProxyTestsTunnelProxy) diff --git a/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h new file mode 100644 index 0000000..ceea833 --- /dev/null +++ b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h @@ -0,0 +1,59 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef REMOTEPROXYTESTSTUNNELPROXY_H +#define REMOTEPROXYTESTSTUNNELPROXY_H + +#include "basetest.h" + +using namespace remoteproxy; +using namespace remoteproxyclient; + +class RemoteProxyTestsTunnelProxy : public BaseTest +{ + Q_OBJECT +public: + explicit RemoteProxyTestsTunnelProxy(QObject *parent = nullptr); + ~RemoteProxyTestsTunnelProxy() = default; + +private slots: + // Basic stuff + void startStopServer(); + + // WebSocket connection API + void getIntrospect(); + void getHello(); + + void apiBasicCalls_data(); + void apiBasicCalls(); + + void apiBasicCallsTcp_data(); + void apiBasicCallsTcp(); + +}; + +#endif // REMOTEPROXYTESTSTUNNELPROXY_H diff --git a/tests/test-tunnelproxy/test-tunnelproxy.pro b/tests/test-tunnelproxy/test-tunnelproxy.pro new file mode 100644 index 0000000..aa34de2 --- /dev/null +++ b/tests/test-tunnelproxy/test-tunnelproxy.pro @@ -0,0 +1,14 @@ +include(../../nymea-remoteproxy.pri) +include(../testbase/testbase.pri) + +CONFIG += testcase +QT += testlib + +TARGET = nymea-remoteproxy-tunnelproxy-tests + +HEADERS += remoteproxyteststunnelproxy.h + +SOURCES += remoteproxyteststunnelproxy.cpp + +target.path = $$[QT_INSTALL_PREFIX]/bin +INSTALLS += target diff --git a/tests/testbase/basetest.cpp b/tests/testbase/basetest.cpp index 42ecbcc..5434282 100644 --- a/tests/testbase/basetest.cpp +++ b/tests/testbase/basetest.cpp @@ -115,8 +115,10 @@ void BaseTest::startServer() QVERIFY(Engine::instance()->running()); QVERIFY(Engine::instance()->developerMode()); - QVERIFY(Engine::instance()->webSocketServer()->running()); - QVERIFY(Engine::instance()->tcpSocketServer()->running()); + QVERIFY(Engine::instance()->webSocketServerProxy()->running()); + QVERIFY(Engine::instance()->tcpSocketServerProxy()->running()); + QVERIFY(Engine::instance()->webSocketServerTunnelProxy()->running()); + QVERIFY(Engine::instance()->tcpSocketServerTunnelProxy()->running()); QVERIFY(Engine::instance()->monitorServer()->running()); } @@ -131,7 +133,7 @@ void BaseTest::stopServer() cleanUpEngine(); } -QVariant BaseTest::invokeWebSocketApiCall(const QString &method, const QVariantMap params, bool remainsConnected) +QVariant BaseTest::invokeWebSocketProxyApiCall(const QString &method, const QVariantMap params, bool remainsConnected) { Q_UNUSED(remainsConnected) @@ -144,7 +146,7 @@ QVariant BaseTest::invokeWebSocketApiCall(const QString &method, const QVariantM QWebSocket *socket = new QWebSocket("proxy-testclient", QWebSocketProtocol::Version13); connect(socket, &QWebSocket::sslErrors, this, &BaseTest::sslErrors); QSignalSpy spyConnection(socket, SIGNAL(connected())); - socket->open(Engine::instance()->webSocketServer()->serverUrl()); + socket->open(Engine::instance()->webSocketServerProxy()->serverUrl()); spyConnection.wait(); if (spyConnection.count() == 0) { return QVariant(); @@ -187,13 +189,13 @@ QVariant BaseTest::invokeWebSocketApiCall(const QString &method, const QVariantM return QVariant(); } -QVariant BaseTest::injectWebSocketData(const QByteArray &data) +QVariant BaseTest::injectWebSocketProxyData(const QByteArray &data) { QWebSocket *socket = new QWebSocket("proxy-testclient", QWebSocketProtocol::Version13); connect(socket, &QWebSocket::sslErrors, this, &BaseTest::sslErrors); QSignalSpy spyConnection(socket, SIGNAL(connected())); - socket->open(Engine::instance()->webSocketServer()->serverUrl()); + socket->open(Engine::instance()->webSocketServerProxy()->serverUrl()); spyConnection.wait(); if (spyConnection.count() == 0) { return QVariant(); @@ -226,7 +228,7 @@ QVariant BaseTest::injectWebSocketData(const QByteArray &data) return QVariant(); } -QVariant BaseTest::invokeTcpSocketApiCall(const QString &method, const QVariantMap params, bool remainsConnected) +QVariant BaseTest::invokeTcpSocketProxyApiCall(const QString &method, const QVariantMap params, bool remainsConnected) { Q_UNUSED(remainsConnected) @@ -241,8 +243,8 @@ QVariant BaseTest::invokeTcpSocketApiCall(const QString &method, const QVariantM QObject::connect(socket, static_cast(&QSslSocket::sslErrors), this, &BaseTest::sslSocketSslErrors); QSignalSpy spyConnection(socket, &QSslSocket::connected); - socket->connectToHostEncrypted(Engine::instance()->tcpSocketServer()->serverUrl().host(), - static_cast(Engine::instance()->tcpSocketServer()->serverUrl().port())); + socket->connectToHostEncrypted(Engine::instance()->tcpSocketServerProxy()->serverUrl().host(), + static_cast(Engine::instance()->tcpSocketServerProxy()->serverUrl().port())); spyConnection.wait(); if (spyConnection.count() == 0) { return QVariant(); @@ -298,8 +300,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); @@ -400,15 +402,216 @@ bool BaseTest::createRemoteConnection(const QString &token, const QString &nonce } -QVariant BaseTest::injectTcpSocketData(const QByteArray &data) +QVariant BaseTest::injectTcpSocketProxyData(const QByteArray &data) { QSslSocket *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()->tcpSocketServer()->serverUrl().host(), - static_cast(Engine::instance()->tcpSocketServer()->serverUrl().port())); + socket->connectToHostEncrypted(Engine::instance()->tcpSocketServerProxy()->serverUrl().host(), + static_cast(Engine::instance()->tcpSocketServerProxy()->serverUrl().port())); + spyConnection.wait(); + if (spyConnection.count() == 0) { + return QVariant(); + } + + QSignalSpy dataSpy(socket, &QSslSocket::readyRead); + socket->write(data + '\n'); + dataSpy.wait(); + // FIXME: check why it waits the full time here + dataSpy.wait(500); + if (dataSpy.count() != 1) { + qWarning() << "No data received"; + return QVariant(); + } + + QByteArray socketData = socket->readAll(); + socket->close(); + socket->deleteLater(); + + // Make sure the response ends with '}\n' + if (!socketData.endsWith("}\n")) { + qWarning() << "JSON data does not end with \"}\n\""; + return QVariant(); + } + + // Make sure the response it a valid JSON string + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(socketData, &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << "JSON parser error" << error.errorString(); + return QVariant(); + } + + m_commandCounter++; + return jsonDoc.toVariant(); +} + +QVariant BaseTest::invokeWebSocketTunnelProxyApiCall(const QString &method, const QVariantMap params, bool remainsConnected) +{ + Q_UNUSED(remainsConnected) + + QVariantMap request; + request.insert("id", m_commandCounter); + request.insert("method", method); + request.insert("params", params); + QJsonDocument jsonDoc = QJsonDocument::fromVariant(request); + + QWebSocket *socket = new QWebSocket("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) { + return QVariant(); + } + + QSignalSpy dataSpy(socket, SIGNAL(textMessageReceived(QString))); + socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact))); + dataSpy.wait(); + + socket->close(); + socket->deleteLater(); + + 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 QVariant(); + } + + // Make sure the response it a valid JSON string + QJsonParseError error; + jsonDoc = QJsonDocument::fromJson(dataSpy.at(i).last().toByteArray(), &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << "JSON parser error" << error.errorString(); + return QVariant(); + } + QVariantMap response = jsonDoc.toVariant().toMap(); + + // skip notifications + if (response.contains("notification")) + continue; + + if (response.value("id").toInt() == m_commandCounter) { + m_commandCounter++; + return jsonDoc.toVariant(); + } + } + + m_commandCounter++; + return QVariant(); +} + +QVariant BaseTest::injectWebSocketTunnelProxyData(const QByteArray &data) +{ + QWebSocket *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) { + return QVariant(); + } + + QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString))); + socket->sendTextMessage(QString(data)); + spy.wait(); + + socket->close(); + socket->deleteLater(); + + for (int i = 0; i < spy.count(); i++) { + // Make sure the response ends with '}\n' + if (!spy.at(i).last().toByteArray().endsWith("}\n")) { + qWarning() << "JSON data does not end with \"}\n\""; + return QVariant(); + } + + // Make sure the response it a valid JSON string + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.at(i).last().toByteArray(), &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << "JSON parser error" << error.errorString(); + return QVariant(); + } + return jsonDoc.toVariant(); + } + m_commandCounter++; + return QVariant(); +} + +QVariant BaseTest::invokeTcpSocketTunnelProxyApiCall(const QString &method, const QVariantMap params, bool remainsConnected) +{ + Q_UNUSED(remainsConnected) + + QVariantMap request; + request.insert("id", m_commandCounter); + request.insert("method", method); + request.insert("params", params); + QJsonDocument jsonDoc = QJsonDocument::fromVariant(request); + + QSslSocket *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 QVariant(); + } + + 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 QVariant(); + } + + QByteArray data = socket->readAll(); + socket->close(); + socket->deleteLater(); + + // Make sure the response ends with '}\n' + if (!data.endsWith("}\n")) { + qWarning() << "JSON data does not end with \"}\n\""; + return QVariant(); + } + + // 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 QVariant(); + } + + QVariantMap response = jsonDoc.toVariant().toMap(); + + if (response.value("id").toInt() == m_commandCounter) { + m_commandCounter++; + return jsonDoc.toVariant(); + } + + m_commandCounter++; + return QVariant(); +} + +QVariant BaseTest::injectTcpSocketTunnelProxyData(const QByteArray &data) +{ + QSslSocket *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 QVariant(); @@ -465,7 +668,6 @@ void BaseTest::initTestCase() "T_xh6pMkOhE6g"; qCDebug(dcApplication()) << "Init test case done."; - //restartEngine(); } void BaseTest::cleanupTestCase() diff --git a/tests/testbase/basetest.h b/tests/testbase/basetest.h index 4643272..b6d8d41 100644 --- a/tests/testbase/basetest.h +++ b/tests/testbase/basetest.h @@ -59,6 +59,9 @@ protected: QUrl m_serverUrl = QUrl("wss://127.0.0.1:1212"); QUrl m_serverUrlTcp = QUrl("ssl://127.0.0.1:1213"); + QUrl m_serverUrlTunnel = QUrl("wss://127.0.0.1:2212"); + QUrl m_serverUrlTunnelTcp = QUrl("ssl://127.0.0.1:2213"); + QSslConfiguration m_sslConfiguration; Authenticator *m_authenticator = nullptr; @@ -79,11 +82,18 @@ protected: void startServer(); void stopServer(); - QVariant invokeWebSocketApiCall(const QString &method, const QVariantMap params = QVariantMap(), bool remainsConnected = true); - QVariant injectWebSocketData(const QByteArray &data); + QVariant invokeWebSocketProxyApiCall(const QString &method, const QVariantMap params = QVariantMap(), bool remainsConnected = true); + QVariant injectWebSocketProxyData(const QByteArray &data); + + QVariant invokeTcpSocketProxyApiCall(const QString &method, const QVariantMap params = QVariantMap(), bool remainsConnected = true); + QVariant injectTcpSocketProxyData(const QByteArray &data); + + QVariant invokeWebSocketTunnelProxyApiCall(const QString &method, const QVariantMap params = QVariantMap(), bool remainsConnected = true); + QVariant injectWebSocketTunnelProxyData(const QByteArray &data); + + QVariant invokeTcpSocketTunnelProxyApiCall(const QString &method, const QVariantMap params = QVariantMap(), bool remainsConnected = true); + QVariant injectTcpSocketTunnelProxyData(const QByteArray &data); - QVariant invokeTcpSocketApiCall(const QString &method, const QVariantMap params = QVariantMap(), bool remainsConnected = true); - QVariant injectTcpSocketData(const QByteArray &data); bool createRemoteConnection(const QString &token, const QString &nonce, QObject *parent); diff --git a/tests/tests.pro b/tests/tests.pro index b1c1bc0..7d077d8 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -1,10 +1,10 @@ include(../nymea-remoteproxy.pri) TEMPLATE=subdirs -SUBDIRS += test-offline +SUBDIRS += test-proxy test-tunnelproxy -online { - message("Online tests enabled") - SUBDIRS += test-online -} +#online { +# message("Online tests enabled") +# SUBDIRS += test-online +#}