diff --git a/README.md b/README.md index 831853c..a8ae436 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,74 @@ Once the other client is here and ready, the server will send a notification to } +## The complete API + + s"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", + "token": "String", + "uuid": "String" + }, + "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" + } + } + }, + "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", + "AuthenticationErrorAuthenticationServerNotResponding" + ], + "BasicType": [ + "Uuid", + "String", + "Int", + "UInt", + "Double", + "Bool", + "Variant", + "Object" + ] + } + + + # License This is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. diff --git a/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp b/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp index 65a7ad1..0e2fe71 100644 --- a/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp +++ b/libnymea-remoteproxy/jsonrpc/authenticationhandler.cpp @@ -65,11 +65,12 @@ void AuthenticationHandler::onAuthenticationFinished() if (authenticationReply->error() != Authenticator::AuthenticationErrorNoError) { qCWarning(dcJsonRpc()) << "Authentication error occured" << authenticationReply->error(); - jsonReply->setSuccess(true); - authenticationReply->proxyClient()->setAuthenticated(true); - } else { - authenticationReply->proxyClient()->setAuthenticated(false); jsonReply->setSuccess(false); + authenticationReply->proxyClient()->setAuthenticated(false); + } else { + // Successfully authenticated + authenticationReply->proxyClient()->setAuthenticated(true); + jsonReply->setSuccess(true); } jsonReply->setData(errorToReply(authenticationReply->error())); diff --git a/libnymea-remoteproxy/jsonrpcserver.cpp b/libnymea-remoteproxy/jsonrpcserver.cpp index cda4660..4fdbb5f 100644 --- a/libnymea-remoteproxy/jsonrpcserver.cpp +++ b/libnymea-remoteproxy/jsonrpcserver.cpp @@ -65,9 +65,9 @@ JsonReply *JsonRpcServer::Hello(const QVariantMap ¶ms, ProxyClient *proxyCli Q_UNUSED(proxyClient) QVariantMap data; - data.insert("server", QCoreApplication::applicationName()); + data.insert("server", SERVER_NAME_STRING); data.insert("name", Engine::instance()->serverName()); - data.insert("version", QCoreApplication::applicationVersion()); + data.insert("version", SERVER_VERSION_STRING); data.insert("apiVersion", API_VERSION_STRING); return createReply(data); @@ -182,12 +182,12 @@ void JsonRpcServer::asyncReplyFinished() sendResponse(proxyClient, reply->commandId(), reply->data()); if (!reply->success()) { // Disconnect this client since the request was not successfully - proxyClient->interface()->killClientConnection(proxyClient->clientId()); + proxyClient->interface()->killClientConnection(proxyClient->clientId(), "API call was not successfully."); } } else { sendErrorResponse(proxyClient, reply->commandId(), "Command timed out"); // Disconnect this client since he requested something that created a timeout - proxyClient->interface()->killClientConnection(proxyClient->clientId()); + proxyClient->interface()->killClientConnection(proxyClient->clientId(), "API call timeouted."); } } @@ -225,7 +225,7 @@ void JsonRpcServer::processData(ProxyClient *proxyClient, const QByteArray &data if(error.error != QJsonParseError::NoError) { qCWarning(dcJsonRpc) << "Failed to parse JSON data" << data << ":" << error.errorString(); sendErrorResponse(proxyClient, -1, QString("Failed to parse JSON data: %1").arg(error.errorString())); - proxyClient->interface()->killClientConnection(proxyClient->clientId()); + proxyClient->interface()->killClientConnection(proxyClient->clientId(), "Invalid JSON data received."); return; } @@ -236,7 +236,7 @@ void JsonRpcServer::processData(ProxyClient *proxyClient, const QByteArray &data if (!success) { qCWarning(dcJsonRpc()) << "Error parsing command. Missing \"id\":" << message; sendErrorResponse(proxyClient, -1, "Error parsing command. Missing 'id'"); - proxyClient->interface()->killClientConnection(proxyClient->clientId()); + proxyClient->interface()->killClientConnection(proxyClient->clientId(), "The id property is missing in the request."); return; } @@ -244,7 +244,7 @@ void JsonRpcServer::processData(ProxyClient *proxyClient, const QByteArray &data if (commandList.count() != 2) { qCWarning(dcJsonRpc) << "Error parsing method.\nGot:" << message.value("method").toString() << "\nExpected: \"Namespace.method\""; sendErrorResponse(proxyClient, commandId, QString("Error parsing method. Got: '%1'', Expected: 'Namespace.method'").arg(message.value("method").toString())); - proxyClient->interface()->killClientConnection(proxyClient->clientId()); + proxyClient->interface()->killClientConnection(proxyClient->clientId(), "Invalid method passed."); return; } @@ -254,13 +254,13 @@ void JsonRpcServer::processData(ProxyClient *proxyClient, const QByteArray &data JsonHandler *handler = m_handlers.value(targetNamespace); if (!handler) { sendErrorResponse(proxyClient, commandId, "No such namespace"); - proxyClient->interface()->killClientConnection(proxyClient->clientId()); + proxyClient->interface()->killClientConnection(proxyClient->clientId(), "No such namespace."); return; } if (!handler->hasMethod(method)) { - sendErrorResponse(proxyClient, commandId, "No such namespace"); - proxyClient->interface()->killClientConnection(proxyClient->clientId()); + sendErrorResponse(proxyClient, commandId, "No such method"); + proxyClient->interface()->killClientConnection(proxyClient->clientId(), "No such method."); return; } @@ -269,7 +269,7 @@ void JsonRpcServer::processData(ProxyClient *proxyClient, const QByteArray &data QPair validationResult = handler->validateParams(method, params); if (!validationResult.first) { sendErrorResponse(proxyClient, commandId, "Invalid params: " + validationResult.second); - proxyClient->interface()->killClientConnection(proxyClient->clientId()); + proxyClient->interface()->killClientConnection(proxyClient->clientId(), "Invalid params passed."); return; } diff --git a/libnymea-remoteproxy/proxyserver.cpp b/libnymea-remoteproxy/proxyserver.cpp index 1f9ce57..e3c5914 100644 --- a/libnymea-remoteproxy/proxyserver.cpp +++ b/libnymea-remoteproxy/proxyserver.cpp @@ -100,7 +100,7 @@ void ProxyServer::onClientDataAvailable(const QUuid &clientId, const QByteArray qCWarning(dcProxyServer()) << "An authenticated client sent data without tunnel connection. This is not allowed."; m_jsonRpcServer->unregisterClient(proxyClient); // The client is authenticated and tries to send data, this is not allowed. - proxyClient->interface()->killClientConnection(proxyClient->clientId()); + proxyClient->interface()->killClientConnection(proxyClient->clientId(), "Data received while authenticated but not remote connected."); return; } diff --git a/libnymea-remoteproxy/transportinterface.h b/libnymea-remoteproxy/transportinterface.h index 3137ef2..33138c7 100644 --- a/libnymea-remoteproxy/transportinterface.h +++ b/libnymea-remoteproxy/transportinterface.h @@ -17,7 +17,7 @@ public: virtual void sendData(const QUuid &clientId, const QByteArray &data) = 0; virtual void sendData(const QList &clients, const QByteArray &data) = 0; - virtual void killClientConnection(const QUuid &clientId) = 0; + virtual void killClientConnection(const QUuid &clientId, const QString &killReason) = 0; signals: void clientConnected(const QUuid &clientId); diff --git a/libnymea-remoteproxy/websocketserver.cpp b/libnymea-remoteproxy/websocketserver.cpp index 044f01d..56a4069 100644 --- a/libnymea-remoteproxy/websocketserver.cpp +++ b/libnymea-remoteproxy/websocketserver.cpp @@ -59,14 +59,14 @@ void WebSocketServer::sendData(const QList &clients, const QByteArray &da } } -void WebSocketServer::killClientConnection(const QUuid &clientId) +void WebSocketServer::killClientConnection(const QUuid &clientId, const QString &killReason) { QWebSocket *client = m_clientList.value(clientId); if (!client) return; - qCWarning(dcWebSocketServer()) << "Kill client connection" << clientId.toString(); - client->close(QWebSocketProtocol::CloseCodeBadOperation); + qCWarning(dcWebSocketServer()) << "Kill client connection" << clientId.toString() << "Reason:" << killReason; + client->close(QWebSocketProtocol::CloseCodeBadOperation, killReason); } void WebSocketServer::onClientConnected() diff --git a/libnymea-remoteproxy/websocketserver.h b/libnymea-remoteproxy/websocketserver.h index 4bffe68..b8a792e 100644 --- a/libnymea-remoteproxy/websocketserver.h +++ b/libnymea-remoteproxy/websocketserver.h @@ -29,7 +29,7 @@ public: void sendData(const QUuid &clientId, const QByteArray &data) override; void sendData(const QList &clients, const QByteArray &data) override; - void killClientConnection(const QUuid &clientId) override; + void killClientConnection(const QUuid &clientId, const QString &killReason) override; private: QUrl m_serverUrl; diff --git a/libnymea-remoteproxyclient/jsonreply.cpp b/libnymea-remoteproxyclient/jsonreply.cpp index bba1dd3..1ab1cf1 100644 --- a/libnymea-remoteproxyclient/jsonreply.cpp +++ b/libnymea-remoteproxyclient/jsonreply.cpp @@ -40,6 +40,7 @@ QVariantMap JsonReply::requestMap() if (!m_params.isEmpty()) request.insert("params", m_params); + m_commandId++; return request; } diff --git a/libnymea-remoteproxyclient/jsonrpcclient.cpp b/libnymea-remoteproxyclient/jsonrpcclient.cpp index 8f76f6c..e145f7b 100644 --- a/libnymea-remoteproxyclient/jsonrpcclient.cpp +++ b/libnymea-remoteproxyclient/jsonrpcclient.cpp @@ -8,37 +8,52 @@ Q_LOGGING_CATEGORY(dcRemoteProxyClientJsonRpcTraffic, "RemoteProxyClientJsonRpcT namespace remoteproxyclient { -JsonRpcClient::JsonRpcClient(QObject *parent) : - QObject(parent) +JsonRpcClient::JsonRpcClient(ProxyConnection *connection, QObject *parent) : + QObject(parent), + m_connection(connection) { } JsonReply *JsonRpcClient::callHello() { - JsonReply *reply = new JsonReply(m_commandId, "RemoteProxy", "Hello", QVariantMap(), this); + qCDebug(dcRemoteProxyClientJsonRpc()) << "Calling" << QString("%1.%2").arg(reply->nameSpace()).arg(reply->method()); + sendRequest(reply->requestMap()); + m_replies.insert(m_commandId, reply); + return reply; +} +JsonReply *JsonRpcClient::callAuthenticate(const QUuid &clientUuid, const QString &clientName, const QString &token) +{ + QVariantMap params; + params.insert("name", clientName); + params.insert("uuid", clientUuid.toString()); + params.insert("token", token); + + JsonReply *reply = new JsonReply(m_commandId, "Authentication", "Authenticate", params, this); + qCDebug(dcRemoteProxyClientJsonRpc()) << "Calling" << QString("%1.%2").arg(reply->nameSpace()).arg(reply->method()) << params; + sendRequest(reply->requestMap()); + m_replies.insert(m_commandId, reply); return reply; } void JsonRpcClient::sendRequest(const QVariantMap &request) { - m_connection->sendData(QJsonDocument::fromVariant(request).toJson(QJsonDocument::Compact) + '\n'); + QByteArray data = QJsonDocument::fromVariant(request).toJson(QJsonDocument::Compact) + '\n'; + qCDebug(dcRemoteProxyClientJsonRpcTraffic()) << "Sending" << qUtf8Printable(data); + m_connection->sendData(data); } void JsonRpcClient::onConnectedChanged(bool connected) { - if (!connected) { - m_serverName = QString(); - m_proxyServerName = QString(); - m_proxyServerVersion = QString(); - m_proxyApiVersion = QString(); - } + Q_UNUSED(connected) } void JsonRpcClient::processData(const QByteArray &data) { + qCDebug(dcRemoteProxyClientJsonRpcTraffic()) << "Received data:" << qUtf8Printable(data); + QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { @@ -52,15 +67,10 @@ void JsonRpcClient::processData(const QByteArray &data) int commandId = dataMap.value("id").toInt(); JsonReply *reply = m_replies.take(commandId); if (reply) { - reply->deleteLater(); - - qCDebug(dcRemoteProxyClientJsonRpcTraffic()) << QString("Got response for %1.%2: %3").arg(reply->nameSpace(), - reply->method(), - QString::fromUtf8(jsonDoc.toJson(QJsonDocument::Indented))); + qCDebug(dcRemoteProxyClientJsonRpcTraffic()) << QString("Got response for %1.%2: %3").arg(reply->nameSpace(), reply->method(), QString::fromUtf8(jsonDoc.toJson(QJsonDocument::Indented))); if (dataMap.value("status").toString() == "error") { qCWarning(dcRemoteProxyClientJsonRpc()) << "Api error happend" << dataMap.value("error").toString(); - return; } reply->setResponse(dataMap); diff --git a/libnymea-remoteproxyclient/jsonrpcclient.h b/libnymea-remoteproxyclient/jsonrpcclient.h index 1c46a39..205af9a 100644 --- a/libnymea-remoteproxyclient/jsonrpcclient.h +++ b/libnymea-remoteproxyclient/jsonrpcclient.h @@ -1,6 +1,7 @@ #ifndef JSONRPCCLIENT_H #define JSONRPCCLIENT_H +#include #include #include #include @@ -17,9 +18,10 @@ class JsonRpcClient : public QObject { Q_OBJECT public: - explicit JsonRpcClient(QObject *parent = nullptr); + explicit JsonRpcClient(ProxyConnection *connection, QObject *parent = nullptr); JsonReply *callHello(); + JsonReply *callAuthenticate(const QUuid &clientUuid, const QString &clientName, const QString &token); private: ProxyConnection *m_connection = nullptr; @@ -28,11 +30,6 @@ private: QHash m_replies; - QString m_serverName; - QString m_proxyServerName; - QString m_proxyServerVersion; - QString m_proxyApiVersion; - void sendRequest(const QVariantMap &request); signals: diff --git a/libnymea-remoteproxyclient/remoteproxyconnection.cpp b/libnymea-remoteproxyclient/remoteproxyconnection.cpp index 460cd71..b5f5d99 100644 --- a/libnymea-remoteproxyclient/remoteproxyconnection.cpp +++ b/libnymea-remoteproxyclient/remoteproxyconnection.cpp @@ -6,9 +6,11 @@ Q_LOGGING_CATEGORY(dcRemoteProxyClientConnectionTraffic, "RemoteProxyClientConne namespace remoteproxyclient { -RemoteProxyConnection::RemoteProxyConnection(ConnectionType connectionType, QObject *parent) : +RemoteProxyConnection::RemoteProxyConnection(const QUuid &clientUuid, const QString &clientName, ConnectionType connectionType, QObject *parent) : QObject(parent), - m_connectionType(connectionType) + m_connectionType(connectionType), + m_clientUuid(clientUuid), + m_clientName(clientName) { } @@ -54,7 +56,12 @@ QString RemoteProxyConnection::errorString() const bool RemoteProxyConnection::isConnected() const { - return m_state == StateConnected || m_state == StateAuthenticating || m_state == StateWaitTunnel || m_state == StateRemoteConnected; + return m_state == StateConnected + || m_state == StateInitializing + || m_state == StateReady + || m_state == StateAuthenticating + || m_state == StateWaitTunnel + || m_state == StateRemoteConnected; } bool RemoteProxyConnection::isRemoteConnected() const @@ -77,6 +84,26 @@ quint16 RemoteProxyConnection::serverPort() const return m_serverPort; } +QString RemoteProxyConnection::serverName() const +{ + return m_serverName; +} + +QString RemoteProxyConnection::proxyServerName() const +{ + return m_proxyServerName; +} + +QString RemoteProxyConnection::proxyServerVersion() const +{ + return m_proxyServerVersion; +} + +QString RemoteProxyConnection::proxyServerApiVersion() const +{ + return m_proxyServerApiVersion; +} + bool RemoteProxyConnection::insecureConnection() const { return m_insecureConnection; @@ -147,6 +174,7 @@ void RemoteProxyConnection::onConnectionChanged(bool isConnected) setState(StateConnected); emit connected(); + setState(StateInitializing); JsonReply *reply = m_jsonClient->callHello(); connect(reply, &JsonReply::finished, this, &RemoteProxyConnection::onHelloFinished); } @@ -155,13 +183,20 @@ void RemoteProxyConnection::onConnectionChanged(bool isConnected) void RemoteProxyConnection::onConnectionDataAvailable(const QByteArray &data) { switch (m_state) { + case StateConnecting: case StateConnected: + case StateInitializing: + case StateReady: + case StateAuthenticating: m_jsonClient->processData(data); + break; case StateRemoteConnected: // Remote data arrived emit dataReady(data); + break; default: - qCDebug(dcRemoteProxyClientConnection()) << "Unhandled: Data reviced" << data; + qCWarning(dcRemoteProxyClientConnection()) << "Unhandled: Data reviced" << data; + break; } } @@ -178,15 +213,35 @@ void RemoteProxyConnection::onConnectionSslError() void RemoteProxyConnection::onHelloFinished() { JsonReply *reply = static_cast(sender()); + reply->deleteLater(); + QVariantMap response = reply->response(); qCDebug(dcRemoteProxyClientConnection()) << "Hello response ready" << response; + + if (response.value("status").toString() != "success") { + qCWarning(dcRemoteProxyClientConnection()) << "Could not get initial information from proxy server."; + m_connection->disconnectServer(); + return; + } + + QVariantMap responseParams = response.value("params").toMap(); + + // TODO: verify success + + m_serverName = response.value("server").toString(); + m_proxyServerName = response.value("name").toString(); + m_proxyServerVersion = response.value("version").toString(); + m_proxyServerApiVersion = response.value("apiVersion").toString(); + + setState(StateReady); + emit ready(); } void RemoteProxyConnection::onAuthenticateFinished() { JsonReply *reply = static_cast(sender()); QVariantMap response = reply->response(); - qCDebug(dcRemoteProxyClientConnection()) << "Hello response ready" << response; + qCDebug(dcRemoteProxyClientConnection()) << "Authentication response ready" << response; } bool RemoteProxyConnection::connectServer(const QHostAddress &serverAddress, quint16 port) @@ -216,9 +271,9 @@ bool RemoteProxyConnection::connectServer(const QHostAddress &serverAddress, qui connect(m_connection, &ProxyConnection::errorOccured, this, &RemoteProxyConnection::onConnectionSocketError); connect(m_connection, &ProxyConnection::sslErrorOccured, this, &RemoteProxyConnection::onConnectionSslError); - m_jsonClient = new JsonRpcClient(this); + m_jsonClient = new JsonRpcClient(m_connection, this); - qCDebug(dcRemoteProxyClientConnection()) << "Connecting to" << QString("%1:%2").arg(serverAddress.toString()).arg(port); + qCDebug(dcRemoteProxyClientConnection()) << "Connecting to" << m_connection->serverUrl().toString(); m_connection->connectServer(serverAddress, port); setState(StateConnecting); @@ -226,6 +281,19 @@ bool RemoteProxyConnection::connectServer(const QHostAddress &serverAddress, qui return true; } +bool RemoteProxyConnection::authenticate(const QString &token) +{ + if (m_state != StateReady) { + qCWarning(dcRemoteProxyClientConnection()) << "Could not authenticate. The connection is not ready"; + return false; + } + + qCDebug(dcRemoteProxyClientConnection()) << "Start authentication using token" << token; + JsonReply *reply = m_jsonClient->callAuthenticate(m_clientUuid, m_clientName, token); + connect(reply, &JsonReply::finished, this, &RemoteProxyConnection::onAuthenticateFinished); + return true; +} + void RemoteProxyConnection::disconnectServer() { if (m_connection) diff --git a/libnymea-remoteproxyclient/remoteproxyconnection.h b/libnymea-remoteproxyclient/remoteproxyconnection.h index d8caad6..4097d69 100644 --- a/libnymea-remoteproxyclient/remoteproxyconnection.h +++ b/libnymea-remoteproxyclient/remoteproxyconnection.h @@ -1,6 +1,7 @@ #ifndef REMOTEPROXYCONNECTOR_H #define REMOTEPROXYCONNECTOR_H +#include #include #include #include @@ -27,6 +28,8 @@ public: enum State { StateConnecting, StateConnected, + StateInitializing, + StateReady, StateAuthenticating, StateWaitTunnel, StateRemoteConnected, @@ -43,11 +46,10 @@ public: }; Q_ENUM(Error) - explicit RemoteProxyConnection(ConnectionType connectionType = ConnectionTypeWebSocket, QObject *parent = nullptr); + explicit RemoteProxyConnection(const QUuid &clientUuid, const QString &clientName, ConnectionType connectionType = ConnectionTypeWebSocket, QObject *parent = nullptr); ~RemoteProxyConnection(); RemoteProxyConnection::State state() const; - RemoteProxyConnection::Error error() const; QString errorString() const; @@ -58,6 +60,11 @@ public: QHostAddress serverAddress() const; quint16 serverPort() const; + QString serverName() const; + QString proxyServerName() const; + QString proxyServerVersion() const; + QString proxyServerApiVersion() const; + bool insecureConnection() const; void setInsecureConnection(bool insecureConnection); @@ -65,16 +72,27 @@ public: private: ConnectionType m_connectionType = ConnectionTypeWebSocket; + QUuid m_clientUuid; + QString m_clientName; + QHostAddress m_serverAddress; quint16 m_serverPort = 443; + State m_state = StateDisconnected; Error m_error = ErrorNoError; + bool m_insecureConnection = false; bool m_remoteConnected = false; JsonRpcClient *m_jsonClient = nullptr; ProxyConnection *m_connection = nullptr; + // Server information + QString m_serverName; + QString m_proxyServerName; + QString m_proxyServerVersion; + QString m_proxyServerApiVersion; + void cleanUp(); void setState(State state); @@ -83,6 +101,7 @@ private: signals: void connected(); void disconnected(); + void ready(); void remoteConnectedChanged(bool remoteConnected); void stateChanged(RemoteProxyConnection::State state); void errorOccured(RemoteProxyConnection::Error error); @@ -100,6 +119,7 @@ private slots: public slots: bool connectServer(const QHostAddress &serverAddress, quint16 port); + bool authenticate(const QString &token); void disconnectServer(); }; diff --git a/libnymea-remoteproxyclient/websocketconnection.cpp b/libnymea-remoteproxyclient/websocketconnection.cpp index 8c5b6ea..d689a61 100644 --- a/libnymea-remoteproxyclient/websocketconnection.cpp +++ b/libnymea-remoteproxyclient/websocketconnection.cpp @@ -7,10 +7,7 @@ namespace remoteproxyclient { WebSocketConnection::WebSocketConnection(QObject *parent) : ProxyConnection(parent) { - m_webSocket = new QWebSocket("libnymea-remoteproxyclient", QWebSocketProtocol::VersionLatest, this); - - if (allowSslErrors()) - m_webSocket->ignoreSslErrors(); + m_webSocket = new QWebSocket("libnymea-remoteproxyclient", QWebSocketProtocol::Version13, this); connect(m_webSocket, &QWebSocket::connected, this, &WebSocketConnection::onConnected); connect(m_webSocket, &QWebSocket::disconnected, this, &WebSocketConnection::onDisconnected); @@ -24,7 +21,7 @@ WebSocketConnection::WebSocketConnection(QObject *parent) : WebSocketConnection::~WebSocketConnection() { - disconnectServer(); + } QUrl WebSocketConnection::serverUrl() const @@ -69,6 +66,7 @@ void WebSocketConnection::onSslError(const QList &errors) foreach (const QSslError sslError, errors) { qCWarning(dcRemoteProxyClientWebSocket()) << " -->" << static_cast(sslError.error()) << sslError.errorString(); } + emit sslErrorOccured(); } } @@ -89,10 +87,14 @@ void WebSocketConnection::onBinaryMessageReceived(const QByteArray &message) void WebSocketConnection::connectServer(const QHostAddress &address, quint16 port) { + if (isConnected()) { + m_webSocket->close(); + } + QUrl url; url.setScheme("wss"); url.setHost(address.toString()); - url.port(port); + url.setPort(port); m_serverUrl = url; diff --git a/nymea-remoteproxy.pri b/nymea-remoteproxy.pri index fe0a1a4..2b077fc 100644 --- a/nymea-remoteproxy.pri +++ b/nymea-remoteproxy.pri @@ -1,10 +1,15 @@ QT *= network websockets QT -= gui -# define protocol versions +# Define versions +SERVER_NAME=nymea-remoteproxy API_VERSION_MAJOR=0 API_VERSION_MINOR=1 -DEFINES += API_VERSION_STRING=\\\"$${API_VERSION_MAJOR}.$${API_VERSION_MINOR}\\\" +SERVER_VERSION=0.0.1 + +DEFINES += SERVER_NAME_STRING=\\\"$${SERVER_NAME}\\\" \ + SERVER_VERSION_STRING=\\\"$${SERVER_VERSION}\\\" \ + API_VERSION_STRING=\\\"$${API_VERSION_MAJOR}.$${API_VERSION_MINOR}\\\" CONFIG += c++11 console @@ -15,8 +20,6 @@ top_srcdir=$$PWD top_builddir=$$shadowed($$PWD) coverage { - message("Building with coverage report") - # Note: this works only if you build in the source dir OBJECTS_DIR = MOC_DIR = diff --git a/nymea-remoteproxy.pro b/nymea-remoteproxy.pro index f1c2f89..7f7c81e 100644 --- a/nymea-remoteproxy.pro +++ b/nymea-remoteproxy.pro @@ -5,3 +5,11 @@ SUBDIRS += server libnymea-remoteproxy libnymea-remoteproxyclient tests server.depends = libnymea-remoteproxy tests.depends = libnymea-remoteproxy libnymea-remoteproxyclient + + +message("----------------------------------------------------------") +message("Building nymea-remoteproxy $${SERVER_VERSION}") +message("----------------------------------------------------------") +message("JSON-RPC API version $${API_VERSION_MAJOR}.$${API_VERSION_MINOR}") +message("Qt version:" $$[QT_VERSION]) +coverage { message("Building with coverage report") } diff --git a/server/main.cpp b/server/main.cpp index 27017c1..71e28f8 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -85,9 +85,9 @@ int main(int argc, char *argv[]) qInstallMessageHandler(consoleLogHandler); QCoreApplication application(argc, argv); - application.setApplicationName("nymea-remoteproxy"); + application.setApplicationName(SERVER_NAME_STRING); application.setOrganizationName("guh"); - application.setApplicationVersion("0.0.1"); + application.setApplicationVersion(SERVER_VERSION_STRING); s_loggingFilters.insert("Engine", true); s_loggingFilters.insert("Application", true); @@ -101,9 +101,15 @@ int main(int argc, char *argv[]) // command line parser QCommandLineParser parser; parser.addHelpOption(); + parser.addVersionOption(); parser.setApplicationDescription(QString("\nThe nymea remote proxy server. This server allowes nymea-cloud users and " "registered nymea deamons to establish a tunnel connection.\n\n" - "Copyright %1 2018 Simon Stürz ").arg(QChar(0xA9))); + "Server version: %1\n" + "API version: %2\n\n" + "Copyright %3 2018 Simon Stürz \n") + .arg(SERVER_VERSION_STRING) + .arg(API_VERSION_STRING) + .arg(QChar(0xA9))); QCommandLineOption logfileOption(QStringList() << "l" << "logging", "Write log file to the given logfile.", "logfile", "/var/log/nymea-remoteproxy.log"); diff --git a/tests/nymea-remoteproxy-tests.cpp b/tests/nymea-remoteproxy-tests.cpp index e5b828d..c30a251 100644 --- a/tests/nymea-remoteproxy-tests.cpp +++ b/tests/nymea-remoteproxy-tests.cpp @@ -112,21 +112,9 @@ QVariant RemoteProxyTests::invokeApiCall(const QString &method, const QVariantMa return QVariant(); } - // QSignalSpy disconnectedSpy(socket, SIGNAL(disconnected())); QSignalSpy dataSpy(socket, SIGNAL(textMessageReceived(QString))); socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact))); dataSpy.wait(); - // if (remainsConnected) { - // disconnectedSpy.wait(1000); - // if (socket->state() != QAbstractSocket::UnconnectedState) { - // qWarning() << "!!!!!!!!!!!!! socket still connected but should be disconnected!"; - // } - // } else { - // disconnectedSpy.wait(); - // if (socket->state() != QAbstractSocket::ConnectedState) { - // qWarning() << "!!!!!!!!!!!!! socket not connected but should be!"; - // } - // } socket->close(); socket->deleteLater(); @@ -365,14 +353,23 @@ void RemoteProxyTests::clientConnection() startServer(); // Connect to the server (insecure disabled) - RemoteProxyConnection *connectorOne = new RemoteProxyConnection(RemoteProxyConnection::ConnectionTypeWebSocket, this); + RemoteProxyConnection *connectorOne = new RemoteProxyConnection(QUuid::createUuid(), "Test client one", RemoteProxyConnection::ConnectionTypeWebSocket, this); connectorOne->setInsecureConnection(true); // Connect to server (insecue enabled for testing) - QSignalSpy spyConnected(connectorOne, &RemoteProxyConnection::connected); - connectorOne->connectServer(QHostAddress::LocalHost, m_port); - spyConnected.wait(); - //QVERIFY(connectorOne->isConnected()); + QSignalSpy readySpy(connectorOne, &RemoteProxyConnection::ready); + QVERIFY(connectorOne->connectServer(QHostAddress::LocalHost, m_port)); + readySpy.wait(); + QVERIFY(readySpy.count() == 1); + QVERIFY(connectorOne->isConnected()); + QVERIFY(connectorOne->state() == RemoteProxyConnection::StateReady); + + // Authenticate + m_authenticator->setTimeoutDuration(100); + m_authenticator->setExpectedAuthenticationError(); + connectorOne->authenticate("foobar"); + + QTest::qWait(1000); // Disconnect and clean up QSignalSpy spyDisconnected(connectorOne, &RemoteProxyConnection::disconnected); diff --git a/tests/nymea-remoteproxy-tests.h b/tests/nymea-remoteproxy-tests.h index 047d04e..f689ab1 100644 --- a/tests/nymea-remoteproxy-tests.h +++ b/tests/nymea-remoteproxy-tests.h @@ -63,11 +63,12 @@ private slots: void authenticate(); // Client lib - void clientConnection(); void sslConfigurations(); - void timeout(); + void clientConnection(); + + public slots: void sslErrors(const QList &) { QWebSocket *socket = static_cast(sender());