First working connection mechanism for tunnel proxy
This commit is contained in:
parent
8f9bdcce13
commit
30d6a9dd43
7
common/common.pri
Normal file
7
common/common.pri
Normal file
@ -0,0 +1,7 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/slipdataprocessor.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/slipdataprocessor.cpp
|
||||
108
common/slipdataprocessor.cpp
Normal file
108
common/slipdataprocessor.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
|
||||
* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with this project.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under contact@nymea.io
|
||||
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "slipdataprocessor.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
QByteArray SlipDataProcessor::deserializeData(const QByteArray &data)
|
||||
{
|
||||
QByteArray deserializedData;
|
||||
// Parse serial data
|
||||
bool escaped = false;
|
||||
for (int i = 0; i < data.length(); i++) {
|
||||
quint8 byte = static_cast<quint8>(data.at(i));
|
||||
|
||||
if (escaped) {
|
||||
if (byte == ProtocolByteTransposedEnd) {
|
||||
deserializedData.append(static_cast<char>(ProtocolByteEnd));
|
||||
} else if (byte == ProtocolByteTransposedEsc) {
|
||||
deserializedData.append(static_cast<char>(ProtocolByteEsc));
|
||||
} else {
|
||||
qWarning() << "Error while deserializing data. Escape character received but the escaped character was not recognized.";
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If escape byte, the next byte has to be a modified byte
|
||||
if (byte == ProtocolByteEsc) {
|
||||
escaped = true;
|
||||
} else {
|
||||
deserializedData.append(static_cast<char>(byte));
|
||||
}
|
||||
}
|
||||
|
||||
return deserializedData;
|
||||
}
|
||||
|
||||
QByteArray SlipDataProcessor::serializeData(const QByteArray &data)
|
||||
{
|
||||
QByteArray serializedData;
|
||||
QDataStream stream(&serializedData, QIODevice::WriteOnly);
|
||||
// stream << static_cast<quint8>(ProtocolByteEnd);
|
||||
|
||||
for (int i = 0; i < data.length(); i++) {
|
||||
quint8 byte = static_cast<quint8>(data.at(i));
|
||||
switch (byte) {
|
||||
case ProtocolByteEnd:
|
||||
stream << static_cast<quint8>(ProtocolByteEsc);
|
||||
stream << static_cast<quint8>(ProtocolByteTransposedEnd);
|
||||
break;
|
||||
case ProtocolByteEsc:
|
||||
stream << static_cast<quint8>(ProtocolByteEsc);
|
||||
stream << static_cast<quint8>(ProtocolByteTransposedEsc);
|
||||
break;
|
||||
default:
|
||||
stream << byte;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the protocol end byte
|
||||
stream << static_cast<quint8>(ProtocolByteEnd);
|
||||
|
||||
return serializedData;
|
||||
}
|
||||
|
||||
SlipDataProcessor::Frame SlipDataProcessor::parseFrame(const QByteArray &data)
|
||||
{
|
||||
Frame frame;
|
||||
frame.socketAddress = static_cast<quint16>(static_cast<quint16>(data.at(0)) << 8 | data.at(1));
|
||||
frame.data = data.right(data.length() - 2);
|
||||
return frame;
|
||||
}
|
||||
|
||||
QByteArray SlipDataProcessor::buildFrame(const Frame &frame)
|
||||
{
|
||||
QByteArray addressData;
|
||||
QDataStream stream(&addressData, QIODevice::WriteOnly);
|
||||
stream << frame.socketAddress;
|
||||
return addressData + frame.data;
|
||||
}
|
||||
64
common/slipdataprocessor.h
Normal file
64
common/slipdataprocessor.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
|
||||
* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with this project.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under contact@nymea.io
|
||||
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef SLIPDATAPROCESSOR_H
|
||||
#define SLIPDATAPROCESSOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDataStream>
|
||||
|
||||
// SLIP: https://tools.ietf.org/html/rfc1055
|
||||
|
||||
class SlipDataProcessor
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
enum ProtocolByte {
|
||||
ProtocolByteEnd = 0xC0,
|
||||
ProtocolByteEsc = 0xDB,
|
||||
ProtocolByteTransposedEnd = 0xDC,
|
||||
ProtocolByteTransposedEsc = 0xDD
|
||||
};
|
||||
Q_ENUM(ProtocolByte)
|
||||
|
||||
typedef struct Frame {
|
||||
quint16 socketAddress;
|
||||
QByteArray data;
|
||||
} Frame;
|
||||
|
||||
explicit SlipDataProcessor() = default;
|
||||
|
||||
static QByteArray deserializeData(const QByteArray &data);
|
||||
static QByteArray serializeData(const QByteArray &data);
|
||||
|
||||
static Frame parseFrame(const QByteArray &data);
|
||||
static QByteArray buildFrame(const Frame &frame);
|
||||
|
||||
};
|
||||
|
||||
#endif // SLIPDATAPROCESSOR_H
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 102 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@ -18,11 +18,8 @@ note over proxy: Server: Assign address for this client socket (0x0001)
|
||||
|
||||
proxy->nymea: SLIP:0x0000: ProxyTunnel.ClientConnected (address: 0x0001)
|
||||
|
||||
proxy<-nymea: SLIP:0x0000: ProxyTunnel.AcceptClient (address: 0x0001)
|
||||
|
||||
proxy->nymea: TunnelProxyErrorNoError
|
||||
|
||||
proxy->client: ProxyTunnel.ConfirmClient(uuid, name, serverUuid)
|
||||
proxy->client: TunnelProxyErrorNoError
|
||||
|
||||
note over client: Connected\nAny incomming and outgoing data will\nbe from the connected nymea instance\nuntil disconnected.
|
||||
|
||||
@ -30,10 +27,14 @@ note over nymea, client: Connected: The client can now communicate with nymea di
|
||||
|
||||
|
||||
|
||||
proxy<-client: "data"
|
||||
proxy<-client: "request data"
|
||||
|
||||
nymea<-proxy: SLIP:0x0001 "data"
|
||||
nymea<-proxy: SLIP:0x0001 "request data"
|
||||
|
||||
nymea->proxy: SLIP:0x0001 "data"
|
||||
nymea->proxy: SLIP:0x0001 "response data"
|
||||
|
||||
proxy->client: "data"
|
||||
proxy->client: "response data"
|
||||
|
||||
nymea->proxy: SLIP:0x0001 "notification data"
|
||||
|
||||
proxy->client: "notification data"
|
||||
|
||||
@ -101,7 +101,7 @@ QPair<bool, QString> JsonHandler::validateReturns(const QString &methodName, con
|
||||
|
||||
void JsonHandler::setDescription(const QString &methodName, const QString &description)
|
||||
{
|
||||
for(int i = 0; i < metaObject()->methodCount(); ++i) {
|
||||
for (int i = 0; i < metaObject()->methodCount(); ++i) {
|
||||
QMetaMethod method = metaObject()->method(i);
|
||||
if (method.name() == methodName) {
|
||||
m_descriptions.insert(methodName, description);
|
||||
@ -113,7 +113,7 @@ void JsonHandler::setDescription(const QString &methodName, const QString &descr
|
||||
|
||||
void JsonHandler::setParams(const QString &methodName, const QVariantMap ¶ms)
|
||||
{
|
||||
for(int i = 0; i < metaObject()->methodCount(); ++i) {
|
||||
for (int i = 0; i < metaObject()->methodCount(); ++i) {
|
||||
QMetaMethod method = metaObject()->method(i);
|
||||
if (method.name() == methodName) {
|
||||
m_params.insert(methodName, params);
|
||||
@ -125,7 +125,7 @@ void JsonHandler::setParams(const QString &methodName, const QVariantMap ¶ms
|
||||
|
||||
void JsonHandler::setReturns(const QString &methodName, const QVariantMap &returns)
|
||||
{
|
||||
for(int i = 0; i < metaObject()->methodCount(); ++i) {
|
||||
for (int i = 0; i < metaObject()->methodCount(); ++i) {
|
||||
QMetaMethod method = metaObject()->method(i);
|
||||
if (method.name() == methodName) {
|
||||
m_returns.insert(methodName, returns);
|
||||
|
||||
@ -47,11 +47,21 @@ TunnelProxyHandler::TunnelProxyHandler(QObject *parent) : JsonHandler(parent)
|
||||
params.insert("serverUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid));
|
||||
setParams("RegisterServer", params);
|
||||
returns.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorRef());
|
||||
returns.insert("slipEnabled", JsonTypes::basicTypeToString(JsonTypes::Bool));
|
||||
setReturns("RegisterServer", returns);
|
||||
|
||||
params.clear(); returns.clear();
|
||||
setDescription("DisconnectClient", "A registered server can ask the remote proxy connection to disconnect a client for whatever reason.");
|
||||
params.insert("socketAddress", JsonTypes::basicTypeToString(JsonTypes::UInt));
|
||||
setParams("DisconnectClient", params);
|
||||
returns.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorRef());
|
||||
setReturns("DisconnectClient", returns);
|
||||
|
||||
|
||||
// Client
|
||||
params.clear(); returns.clear();
|
||||
setDescription("RegisterClient", "Register a new TunnelProxy client on TunnelProxy server with the given serverUuid..");
|
||||
setDescription("RegisterClient", "Register a new TunnelProxy client on TunnelProxy server with the given serverUuid. "
|
||||
"On success, the remote connection has been accepted and any further data will come from the connected server.");
|
||||
params.insert("clientName", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
params.insert("clientUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid));
|
||||
params.insert("serverUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid));
|
||||
@ -64,10 +74,11 @@ TunnelProxyHandler::TunnelProxyHandler(QObject *parent) : JsonHandler(parent)
|
||||
// Server
|
||||
params.clear(); returns.clear();
|
||||
setDescription("ClientConnected", "Emitted whenever a new client has been connected to a registered server. "
|
||||
"Only tunnel proxy clients registered as server will receive this notification.");
|
||||
params.insert("clientId", JsonTypes::basicTypeToString(JsonTypes::UInt));
|
||||
params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
params.insert("address", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
"Only tunnel proxy clients registered as server will receive this notification. The socket address will be used for framing.");
|
||||
params.insert("clientName", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
params.insert("clientUuid", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
params.insert("clientPeerAddress", JsonTypes::basicTypeToString(JsonTypes::String));
|
||||
params.insert("socketAddress", JsonTypes::basicTypeToString(JsonTypes::UInt));
|
||||
setParams("ClientConnected", params);
|
||||
|
||||
params.clear(); returns.clear();
|
||||
@ -100,9 +111,21 @@ JsonReply *TunnelProxyHandler::RegisterServer(const QVariantMap ¶ms, Transpo
|
||||
|
||||
QVariantMap response;
|
||||
response.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorToString(error));
|
||||
response.insert("slipEnabled", error == TunnelProxyServer::TunnelProxyErrorNoError);
|
||||
return createReply("RegisterServer", response);
|
||||
}
|
||||
|
||||
JsonReply *TunnelProxyHandler::DisconnectClient(const QVariantMap ¶ms, TransportClient *transportClient)
|
||||
{
|
||||
qCDebug(dcJsonRpc()) << name() << "disconnect client requested" << params << transportClient;
|
||||
quint16 socketAddress = static_cast<quint16>(params.value("socketAddress").toUInt());
|
||||
TunnelProxyServer::TunnelProxyError error = Engine::instance()->tunnelProxyServer()->disconnectClient(transportClient->clientId(), socketAddress);
|
||||
|
||||
QVariantMap response;
|
||||
response.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorToString(error));
|
||||
return createReply("DisconnectClient", response);
|
||||
}
|
||||
|
||||
JsonReply *TunnelProxyHandler::RegisterClient(const QVariantMap ¶ms, TransportClient *transportClient)
|
||||
{
|
||||
qCDebug(dcJsonRpc()) << name() << "register client" << params << transportClient;
|
||||
@ -122,9 +145,10 @@ JsonReply *TunnelProxyHandler::RegisterClient(const QVariantMap ¶ms, Transpo
|
||||
|
||||
QVariantMap response;
|
||||
response.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorToString(error));
|
||||
return createReply("RegisterServer", response);
|
||||
return createReply("RegisterClient", response);
|
||||
}
|
||||
|
||||
|
||||
//JsonReply *TunnelProxyHandler::RemoveClient(const QVariantMap ¶ms, TransportClient *transportClient)
|
||||
//{
|
||||
|
||||
|
||||
@ -46,9 +46,9 @@ public:
|
||||
QString name() const override;
|
||||
|
||||
Q_INVOKABLE JsonReply *RegisterServer(const QVariantMap ¶ms, TransportClient *transportClient);
|
||||
Q_INVOKABLE JsonReply *DisconnectClient(const QVariantMap ¶ms, TransportClient *transportClient);
|
||||
Q_INVOKABLE JsonReply *RegisterClient(const QVariantMap ¶ms, TransportClient *transportClient);
|
||||
|
||||
// Q_INVOKABLE JsonReply *RemoveClient(const QVariantMap ¶ms, TransportClient *transportClient);
|
||||
|
||||
signals:
|
||||
void ClientConnected(const QVariantMap ¶ms, TransportClient *transportClient);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
include(../nymea-remoteproxy.pri)
|
||||
include(../common/common.pri)
|
||||
|
||||
TEMPLATE = lib
|
||||
TARGET = nymea-remoteproxy
|
||||
|
||||
@ -130,16 +130,16 @@ QList<QByteArray> ProxyClient::processData(const QByteArray &data)
|
||||
QList<QByteArray> packages;
|
||||
|
||||
// Handle packet fragmentation
|
||||
m_dataBuffers.append(data);
|
||||
int splitIndex = m_dataBuffers.indexOf("}\n{");
|
||||
m_dataBuffer.append(data);
|
||||
int splitIndex = m_dataBuffer.indexOf("}\n{");
|
||||
while (splitIndex > -1) {
|
||||
packages.append(m_dataBuffers.left(splitIndex + 1));
|
||||
m_dataBuffers = m_dataBuffers.right(m_dataBuffers.length() - splitIndex - 2);
|
||||
splitIndex = m_dataBuffers.indexOf("}\n{");
|
||||
packages.append(m_dataBuffer.left(splitIndex + 1));
|
||||
m_dataBuffer = m_dataBuffer.right(m_dataBuffer.length() - splitIndex - 2);
|
||||
splitIndex = m_dataBuffer.indexOf("}\n{");
|
||||
}
|
||||
if (m_dataBuffers.trimmed().endsWith("}")) {
|
||||
packages.append(m_dataBuffers);
|
||||
m_dataBuffers.clear();
|
||||
if (m_dataBuffer.trimmed().endsWith("}")) {
|
||||
packages.append(m_dataBuffer);
|
||||
m_dataBuffer.clear();
|
||||
}
|
||||
|
||||
return packages;
|
||||
|
||||
@ -29,6 +29,8 @@
|
||||
#include "jsonrpcserver.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "jsonrpc/jsontypes.h"
|
||||
#include "transportclient.h"
|
||||
#include "slipdataprocessor.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
@ -135,7 +137,14 @@ void JsonRpcServer::sendResponse(TransportClient *client, int commandId, const Q
|
||||
|
||||
QByteArray data = QJsonDocument::fromVariant(response).toJson(QJsonDocument::Compact);
|
||||
qCDebug(dcJsonRpcTraffic()) << "Sending data:" << data;
|
||||
client->sendData(data);
|
||||
if (client->slipEnabled()) {
|
||||
SlipDataProcessor::Frame frame;
|
||||
frame.socketAddress = 0x0000;
|
||||
frame.data = data;
|
||||
client->sendData(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
|
||||
} else {
|
||||
client->sendData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void JsonRpcServer::sendErrorResponse(TransportClient *client, int commandId, const QString &error)
|
||||
@ -147,7 +156,14 @@ void JsonRpcServer::sendErrorResponse(TransportClient *client, int commandId, co
|
||||
|
||||
QByteArray data = QJsonDocument::fromVariant(errorResponse).toJson(QJsonDocument::Compact);
|
||||
qCDebug(dcJsonRpcTraffic()) << "Sending data:" << data;
|
||||
client->sendData(data);
|
||||
if (client->slipEnabled()) {
|
||||
SlipDataProcessor::Frame frame;
|
||||
frame.socketAddress = 0x0000;
|
||||
frame.data = data;
|
||||
client->sendData(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
|
||||
} else {
|
||||
client->sendData(data);
|
||||
}
|
||||
}
|
||||
|
||||
QString JsonRpcServer::formatAssertion(const QString &targetNamespace, const QString &method, JsonHandler *handler, const QVariantMap &data) const
|
||||
@ -239,18 +255,21 @@ void JsonRpcServer::processDataPackage(TransportClient *transportClient, const Q
|
||||
Q_ASSERT_X((targetNamespace == "RemoteProxy" && method == "Introspect") || handler->validateReturns(method, reply->data()).first
|
||||
,"validating return value", formatAssertion(targetNamespace, method, handler, reply->data()).toLatin1().data());
|
||||
|
||||
// QPair<bool, QString> returnValidation = reply->handler()->validateReturns(reply->method(), reply->data());
|
||||
// if (!returnValidation.first) {
|
||||
// qCWarning(dcJsonRpc()) << "Return value validation failed of sync reply. This should never happen. Please check the source code.";
|
||||
// }
|
||||
|
||||
reply->setClientId(transportClient->clientId());
|
||||
reply->setCommandId(commandId);
|
||||
sendResponse(transportClient, commandId, reply->data());
|
||||
|
||||
// TODO: check if the client should be disconnected after this response
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
// If the server decided to kill the connection after the response, do it now
|
||||
if (transportClient->killConnectionRequested()) {
|
||||
transportClient->killConnection(transportClient->killConnectionReason());
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable SLIP from now on since requested
|
||||
if (transportClient->slipAfterResponseEnabled()) {
|
||||
transportClient->setSlipEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,7 +344,7 @@ void JsonRpcServer::processData(TransportClient *transportClient, const QByteArr
|
||||
if (!m_clients.contains(transportClient))
|
||||
return;
|
||||
|
||||
qCDebug(dcJsonRpcTraffic()) << "Incoming data from" << transportClient << ": " << data;
|
||||
qCDebug(dcJsonRpcTraffic()) << "Incoming data from" << transportClient << ": " << qUtf8Printable(data);
|
||||
|
||||
// Handle package fragmentation
|
||||
QList<QByteArray> packages = transportClient->processData(data);
|
||||
@ -350,8 +369,16 @@ void JsonRpcServer::sendNotification(const QString &nameSpace, const QString &me
|
||||
notification.insert("params", params);
|
||||
|
||||
QByteArray data = QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact);
|
||||
qCDebug(dcJsonRpcTraffic()) << "Sending notification:" << data;
|
||||
transportClient->sendData(data);
|
||||
if (transportClient->slipEnabled()) {
|
||||
SlipDataProcessor::Frame frame;
|
||||
frame.socketAddress = 0x0000;
|
||||
frame.data = data;
|
||||
qCDebug(dcJsonRpcTraffic()) << "Sending notification frame:" <<frame.socketAddress << qUtf8Printable(frame.data);
|
||||
transportClient->sendData(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
|
||||
} else {
|
||||
qCDebug(dcJsonRpcTraffic()) << "Sending notification:" << data;
|
||||
transportClient->sendData(data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -69,12 +69,13 @@ private:
|
||||
|
||||
QString formatAssertion(const QString &targetNamespace, const QString &method, JsonHandler *handler, const QVariantMap &data) const;
|
||||
|
||||
void processDataPackage(TransportClient *transportClient, const QByteArray &data);
|
||||
|
||||
private slots:
|
||||
void asyncReplyFinished();
|
||||
|
||||
public slots:
|
||||
void processDataPackage(TransportClient *transportClient, const QByteArray &data);
|
||||
|
||||
// Client registration for JSON RPC traffic
|
||||
void registerClient(TransportClient *transportClient);
|
||||
void unregisterClient(TransportClient *transportClient);
|
||||
|
||||
@ -65,7 +65,6 @@ void TcpSocketServer::killClientConnection(const QUuid &clientId, const QString
|
||||
return;
|
||||
|
||||
qCWarning(dcTcpSocketServer()) << "Killing client connection" << clientId.toString() << "Reason:" << killReason;
|
||||
client->flush();
|
||||
client->close();
|
||||
}
|
||||
|
||||
|
||||
@ -61,6 +61,42 @@ QString TransportClient::creationTimeString() const
|
||||
return QDateTime::fromMSecsSinceEpoch(creationTime() * 1000).toString("dd.MM.yyyy hh:mm:ss");
|
||||
}
|
||||
|
||||
void TransportClient::killConnectionAfterResponse(const QString &killConnectionReason)
|
||||
{
|
||||
m_killConnectionRequested = true;
|
||||
m_killConnectionReason = killConnectionReason;
|
||||
}
|
||||
|
||||
bool TransportClient::killConnectionRequested() const
|
||||
{
|
||||
return m_killConnectionRequested;
|
||||
}
|
||||
|
||||
QString TransportClient::killConnectionReason() const
|
||||
{
|
||||
return m_killConnectionReason;
|
||||
}
|
||||
|
||||
void TransportClient::enableSlipAfterResponse()
|
||||
{
|
||||
m_slipAfterResponseEnabled = true;
|
||||
}
|
||||
|
||||
bool TransportClient::slipAfterResponseEnabled() const
|
||||
{
|
||||
return m_slipAfterResponseEnabled;
|
||||
}
|
||||
|
||||
bool TransportClient::slipEnabled() const
|
||||
{
|
||||
return m_slipEnabled;
|
||||
}
|
||||
|
||||
void TransportClient::setSlipEnabled(bool slipEnabled)
|
||||
{
|
||||
m_slipEnabled = slipEnabled;
|
||||
}
|
||||
|
||||
TransportInterface *TransportClient::interface() const
|
||||
{
|
||||
return m_interface;
|
||||
@ -108,7 +144,7 @@ void TransportClient::addTxDataCount(int dataCount)
|
||||
|
||||
int TransportClient::bufferSize() const
|
||||
{
|
||||
return m_dataBuffers.size();
|
||||
return m_dataBuffer.size();
|
||||
}
|
||||
|
||||
int TransportClient::generateMessageId()
|
||||
|
||||
@ -50,6 +50,18 @@ public:
|
||||
uint creationTime() const;
|
||||
QString creationTimeString() const;
|
||||
|
||||
// Schedule a disconnect after the response
|
||||
void killConnectionAfterResponse(const QString &killConnectionReason);
|
||||
bool killConnectionRequested() const;
|
||||
QString killConnectionReason() const;
|
||||
|
||||
// Schedule SLIP enable after response
|
||||
void enableSlipAfterResponse();
|
||||
bool slipAfterResponseEnabled() const;
|
||||
|
||||
bool slipEnabled() const;
|
||||
void setSlipEnabled(bool slipEnabled);
|
||||
|
||||
TransportInterface *interface() const;
|
||||
|
||||
// Properties from auth request
|
||||
@ -85,7 +97,13 @@ protected:
|
||||
QString m_name;
|
||||
QUuid m_uuid;
|
||||
|
||||
QByteArray m_dataBuffers;
|
||||
QByteArray m_dataBuffer;
|
||||
|
||||
bool m_killConnectionRequested = false;
|
||||
QString m_killConnectionReason;
|
||||
|
||||
bool m_slipAfterResponseEnabled = false;
|
||||
bool m_slipEnabled = false;
|
||||
|
||||
// Json data information
|
||||
int m_messageId = 0;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#include "tunnelproxyclient.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "server/transportinterface.h"
|
||||
#include "slipdataprocessor.h"
|
||||
|
||||
namespace remoteproxy {
|
||||
|
||||
@ -23,35 +25,44 @@ void TunnelProxyClient::setType(Type type)
|
||||
emit typeChanged(m_type);
|
||||
}
|
||||
|
||||
bool TunnelProxyClient::slipEnabled() const
|
||||
{
|
||||
return m_slipEnabled;
|
||||
}
|
||||
|
||||
void TunnelProxyClient::setSlipEnabled(bool slipEnabled)
|
||||
{
|
||||
m_slipEnabled = slipEnabled;
|
||||
}
|
||||
|
||||
QList<QByteArray> TunnelProxyClient::processData(const QByteArray &data)
|
||||
{
|
||||
// TODO: unescape if this data is for the json handler
|
||||
QList<QByteArray> packages;
|
||||
|
||||
// Parse packages depending on the encoded
|
||||
if (m_slipEnabled) {
|
||||
// Read each byte until we get END byte, then unescape the package
|
||||
for (int i = 0; i < data.length(); i++) {
|
||||
quint8 byte = static_cast<quint8>(data.at(i));
|
||||
if (byte == SlipDataProcessor::ProtocolByteEnd) {
|
||||
// If there is no data...continue since it might be a starting END byte
|
||||
if (m_dataBuffer.isEmpty())
|
||||
continue;
|
||||
|
||||
QByteArray frame = SlipDataProcessor::deserializeData(m_dataBuffer);
|
||||
if (frame.isNull()) {
|
||||
qCWarning(dcTunnelProxyServerTraffic()) << "Received inconsistant SLIP encoded message. Ignoring data...";
|
||||
} else {
|
||||
qCDebug(dcTunnelProxyServerTraffic()) << "Frame received";
|
||||
packages.append(m_dataBuffer);
|
||||
}
|
||||
} else {
|
||||
m_dataBuffer.append(data.at(i));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle json packet fragmentation
|
||||
m_dataBuffers.append(data);
|
||||
int splitIndex = m_dataBuffers.indexOf("}\n{");
|
||||
m_dataBuffer.append(data);
|
||||
int splitIndex = m_dataBuffer.indexOf("}\n{");
|
||||
while (splitIndex > -1) {
|
||||
packages.append(m_dataBuffers.left(splitIndex + 1));
|
||||
m_dataBuffers = m_dataBuffers.right(m_dataBuffers.length() - splitIndex - 2);
|
||||
splitIndex = m_dataBuffers.indexOf("}\n{");
|
||||
packages.append(m_dataBuffer.left(splitIndex + 1));
|
||||
m_dataBuffer = m_dataBuffer.right(m_dataBuffer.length() - splitIndex - 2);
|
||||
splitIndex = m_dataBuffer.indexOf("}\n{");
|
||||
}
|
||||
if (m_dataBuffers.trimmed().endsWith("}")) {
|
||||
packages.append(m_dataBuffers);
|
||||
m_dataBuffers.clear();
|
||||
if (m_dataBuffer.trimmed().endsWith("}")) {
|
||||
packages.append(m_dataBuffer);
|
||||
m_dataBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,9 +23,6 @@ public:
|
||||
Type type() const;
|
||||
void setType(Type type);
|
||||
|
||||
bool slipEnabled() const;
|
||||
void setSlipEnabled(bool slipEnabled);
|
||||
|
||||
// Json server methods
|
||||
QList<QByteArray> processData(const QByteArray &data) override;
|
||||
|
||||
@ -34,7 +31,6 @@ signals:
|
||||
|
||||
private:
|
||||
Type m_type = TypeNone;
|
||||
bool m_slipEnabled = false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -66,6 +66,16 @@ QUuid TunnelProxyClientConnection::serverUuid() const
|
||||
return m_serverUuid;
|
||||
}
|
||||
|
||||
quint16 TunnelProxyClientConnection::socketAddress() const
|
||||
{
|
||||
return m_socketAddress;
|
||||
}
|
||||
|
||||
void TunnelProxyClientConnection::setSocketAddress(quint16 socketAddress)
|
||||
{
|
||||
m_socketAddress = socketAddress;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, TunnelProxyClientConnection *clientConnection)
|
||||
{
|
||||
debug.nospace() << "TunnelProxyClientConnection(";
|
||||
|
||||
@ -51,6 +51,9 @@ public:
|
||||
QString clientName() const;
|
||||
QUuid serverUuid() const;
|
||||
|
||||
quint16 socketAddress() const;
|
||||
void setSocketAddress(quint16 socketAddress);
|
||||
|
||||
private:
|
||||
TransportClient *m_transportClient = nullptr;
|
||||
TunnelProxyServerConnection *m_serverConnection = nullptr;
|
||||
@ -58,7 +61,7 @@ private:
|
||||
QUuid m_clientUuid;
|
||||
QString m_clientName;
|
||||
QUuid m_serverUuid;
|
||||
|
||||
quint16 m_socketAddress = 0xFFFF;
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, TunnelProxyClientConnection *clientConnection);
|
||||
|
||||
@ -32,6 +32,8 @@
|
||||
#include "tunnelproxyserverconnection.h"
|
||||
#include "tunnelproxyclientconnection.h"
|
||||
|
||||
#include "../common/slipdataprocessor.h"
|
||||
|
||||
namespace remoteproxy {
|
||||
|
||||
TunnelProxyServer::TunnelProxyServer(QObject *parent) :
|
||||
@ -86,12 +88,14 @@ TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerServer(const QUui
|
||||
TunnelProxyClient *tunnelProxyClient = m_proxyClients.value(clientId);
|
||||
if (!tunnelProxyClient) {
|
||||
qCWarning(dcTunnelProxyServer()) << "There is no client with client uuid" << clientId.toString();
|
||||
tunnelProxyClient->killConnectionAfterResponse("Internal server error");
|
||||
return TunnelProxyServer::TunnelProxyErrorInternalServerError;
|
||||
}
|
||||
|
||||
// Make sure this client has not been registered as client or re-registration has been called...
|
||||
if (tunnelProxyClient->type() != TunnelProxyClient::TypeNone) {
|
||||
qCWarning(dcTunnelProxyServer()) << "Client tried to register as server but has already been registerd as" << tunnelProxyClient->type();
|
||||
tunnelProxyClient->killConnectionAfterResponse("Already registered");
|
||||
return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
|
||||
}
|
||||
|
||||
@ -100,7 +104,7 @@ TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerServer(const QUui
|
||||
tunnelProxyClient->setName(serverName);
|
||||
|
||||
// Enable SLIP from now on
|
||||
// tunnelProxyClient->setSlipEnabled(true);
|
||||
tunnelProxyClient->enableSlipAfterResponse();
|
||||
|
||||
TunnelProxyServerConnection *serverConnection = new TunnelProxyServerConnection(tunnelProxyClient, serverUuid, serverName, this);
|
||||
m_tunnelProxyServerConnections.insert(serverUuid, serverConnection);
|
||||
@ -110,22 +114,23 @@ TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerServer(const QUui
|
||||
|
||||
TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerClient(const QUuid &clientId, const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid)
|
||||
{
|
||||
qCDebug(dcTunnelProxyServer()) << "Register new client" << m_proxyClients.value(clientId) << clientName << clientUuid.toString() << "--> server" << serverUuid.toString();
|
||||
|
||||
TunnelProxyClient *tunnelProxyClient = m_proxyClients.value(clientId);
|
||||
if (!tunnelProxyClient) {
|
||||
qCWarning(dcTunnelProxyServer()) << "There is no client with client uuid" << clientId.toString();
|
||||
tunnelProxyClient->killConnectionAfterResponse("Internal server error");
|
||||
return TunnelProxyServer::TunnelProxyErrorInternalServerError;
|
||||
}
|
||||
|
||||
// Make sure this client has not been registered as client or re-registration has been called...
|
||||
if (tunnelProxyClient->type() != TunnelProxyClient::TypeNone) {
|
||||
qCWarning(dcTunnelProxyServer()) << "Client tried to register as client but has already been registerd as" << tunnelProxyClient->type();
|
||||
tunnelProxyClient->killConnectionAfterResponse("Already registered");
|
||||
return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
|
||||
}
|
||||
|
||||
if (m_tunnelProxyClientConnections.contains(clientUuid)) {
|
||||
qCWarning(dcTunnelProxyServer()) << "There is a client already registered with client uuid" << clientUuid.toString();
|
||||
tunnelProxyClient->killConnectionAfterResponse("Already registered");
|
||||
return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
|
||||
}
|
||||
|
||||
@ -133,6 +138,7 @@ TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerClient(const QUui
|
||||
TunnelProxyServerConnection *serverConnection = m_tunnelProxyServerConnections.value(serverUuid);
|
||||
if (!serverConnection) {
|
||||
qCWarning(dcTunnelProxyServer()) << "There is no server registered with server uuid" << serverUuid.toString();
|
||||
tunnelProxyClient->killConnectionAfterResponse("Unknown server");
|
||||
return TunnelProxyServer::TunnelProxyErrorServerNotFound;
|
||||
}
|
||||
|
||||
@ -147,8 +153,47 @@ TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerClient(const QUui
|
||||
qCDebug(dcTunnelProxyServer()) << "Register client" << clientConnection << "-->" << serverConnection;
|
||||
serverConnection->registerClientConnection(clientConnection);
|
||||
|
||||
// TODO: wait for te aprovement from the server
|
||||
// Tell the server a new client want's to connect
|
||||
QVariantMap params;
|
||||
params.insert("clientName", tunnelProxyClient->name());
|
||||
params.insert("clientUuid", tunnelProxyClient->uuid().toString());
|
||||
params.insert("clientPeerAddress", tunnelProxyClient->peerAddress().toString());
|
||||
params.insert("socketAddress", clientConnection->socketAddress());
|
||||
m_jsonRpcServer->sendNotification("TunnelProxy", "ClientConnected", params, serverConnection->transportClient());
|
||||
|
||||
// Note: check if a confirmation from the server would be needed, for rejection or limit or something. For now they are directly connected.
|
||||
|
||||
return TunnelProxyServer::TunnelProxyErrorNoError;
|
||||
}
|
||||
|
||||
TunnelProxyServer::TunnelProxyError TunnelProxyServer::disconnectClient(const QUuid &clientId, quint16 socketAddress)
|
||||
{
|
||||
TunnelProxyClient *tunnelProxyClient = m_proxyClients.value(clientId);
|
||||
if (!tunnelProxyClient) {
|
||||
qCWarning(dcTunnelProxyServer()) << "There is no client with client uuid" << clientId.toString();
|
||||
return TunnelProxyServer::TunnelProxyErrorInternalServerError;
|
||||
}
|
||||
|
||||
if (tunnelProxyClient->type() != TunnelProxyClient::TypeServer) {
|
||||
qCWarning(dcTunnelProxyServer()) << "Client tried to disconnect a client but has not been registerd as server" << tunnelProxyClient->type();
|
||||
tunnelProxyClient->killConnectionAfterResponse("Already registered");
|
||||
return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
|
||||
}
|
||||
|
||||
TunnelProxyServerConnection *serverConnection = m_tunnelProxyServerConnections.value(tunnelProxyClient->uuid());
|
||||
if (!serverConnection) {
|
||||
qCWarning(dcTunnelProxyServer()) << "Could not find server connection for" << tunnelProxyClient;
|
||||
tunnelProxyClient->killConnectionAfterResponse("Internal server error");
|
||||
return TunnelProxyServer::TunnelProxyErrorInternalServerError;
|
||||
}
|
||||
|
||||
TunnelProxyClientConnection *clientConnection = serverConnection->getClientConnection(socketAddress);
|
||||
if (!clientConnection) {
|
||||
qCWarning(dcTunnelProxyServer()) << "Could not find client connection for socket address" << socketAddress << "on" << serverConnection;
|
||||
return TunnelProxyServer::TunnelProxyErrorUnknownSocketAddress;
|
||||
}
|
||||
|
||||
clientConnection->transportClient()->killConnection("Server requested disconnect.");
|
||||
return TunnelProxyServer::TunnelProxyErrorNoError;
|
||||
}
|
||||
|
||||
@ -202,8 +247,11 @@ void TunnelProxyServer::onClientDisconnected(const QUuid &clientId)
|
||||
qCWarning(dcTunnelProxyServer()) << "Could not find server connection for disconnected tunnel proxy client claiming to be a server.";
|
||||
} else {
|
||||
|
||||
|
||||
// TODO: kill all related clients
|
||||
foreach (TunnelProxyClientConnection *clientConnection, serverConnection->clientConnections()) {
|
||||
serverConnection->unregisterClientConnection(clientConnection);
|
||||
clientConnection->setSocketAddress(0xFFFF);
|
||||
clientConnection->transportClient()->killConnection("Server disconnected");
|
||||
}
|
||||
|
||||
serverConnection->deleteLater();
|
||||
}
|
||||
@ -215,8 +263,10 @@ void TunnelProxyServer::onClientDisconnected(const QUuid &clientId)
|
||||
qCWarning(dcTunnelProxyServer()) << "Could not find client connection for disconnected tunnel proxy client claiming to be a client.";
|
||||
} else {
|
||||
if (clientConnection->serverConnection()) {
|
||||
QVariantMap params;
|
||||
params.insert("socketAddress", clientConnection->socketAddress());
|
||||
m_jsonRpcServer->sendNotification("TunnelProxy", "ClientDisconnected", params, clientConnection->serverConnection()->transportClient());
|
||||
clientConnection->serverConnection()->unregisterClientConnection(clientConnection);
|
||||
// TODO: Send client disconnected to the server
|
||||
}
|
||||
|
||||
clientConnection->deleteLater();
|
||||
@ -239,23 +289,63 @@ void TunnelProxyServer::onClientDataAvailable(const QUuid &clientId, const QByte
|
||||
}
|
||||
|
||||
qCDebug(dcTunnelProxyServerTraffic()) << "Client data available" << tunnelProxyClient << qUtf8Printable(data);
|
||||
|
||||
if (tunnelProxyClient->type() == TunnelProxyClient::TypeClient) {
|
||||
// Send the data to the server using slip + frame
|
||||
// Send the data to the server using slip encoded frame
|
||||
TunnelProxyClientConnection *clientConnection = m_tunnelProxyClientConnections.value(tunnelProxyClient->uuid());
|
||||
if (!clientConnection) {
|
||||
qCWarning(dcTunnelProxyServer()) << "Could not find a client connection for client uuid" << tunnelProxyClient->uuid().toString();
|
||||
// FIXME: check what do with this client
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT_X(clientConnection->serverConnection(), "TunnelProxyServer", "The client has not been registered to a server connection");
|
||||
|
||||
SlipDataProcessor::Frame frame;
|
||||
frame.socketAddress = clientConnection->socketAddress();
|
||||
frame.data = data;
|
||||
qCDebug(dcTunnelProxyServerTraffic()) << "Write client data to server using socket address" << clientConnection->socketAddress();
|
||||
clientConnection->serverConnection()->transportClient()->sendData(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
|
||||
} else if (tunnelProxyClient->type() == TunnelProxyClient::TypeServer) {
|
||||
// Data coming from a connected server connection
|
||||
if (tunnelProxyClient->slipEnabled()) {
|
||||
// Unpack SLIP data, get address, pipe to client or give it to the json rpc server if address 0x0000
|
||||
// Handle package fragmentation
|
||||
QList<QByteArray> frames = tunnelProxyClient->processData(data);
|
||||
foreach (const QByteArray &frameData, frames) {
|
||||
SlipDataProcessor::Frame frame = SlipDataProcessor::parseFrame(frameData);
|
||||
|
||||
if (frame.socketAddress == 0x0000) {
|
||||
qCDebug(dcTunnelProxyServerTraffic()) << "Received frame for the JSON server" << tunnelProxyClient;
|
||||
m_jsonRpcServer->processDataPackage(tunnelProxyClient, frame.data);
|
||||
} else {
|
||||
// This data seems to be for a client with the given address
|
||||
TunnelProxyServerConnection *serverConnection = m_tunnelProxyServerConnections.value(tunnelProxyClient->uuid());
|
||||
if (!serverConnection) {
|
||||
qCWarning(dcTunnelProxyServer()) << "Could not find server connection for" << tunnelProxyClient;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the client and send the data to the client
|
||||
TunnelProxyClientConnection *clientConnection = serverConnection->getClientConnection(frame.socketAddress);
|
||||
if (!clientConnection) {
|
||||
qCWarning(dcTunnelProxyServer()) << "The server connection wants to send data to a client connection which has not been registered to the server.";
|
||||
// FIXME: tell the server this client does not exist
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcTunnelProxyServerTraffic()) << "Sending data to" << clientConnection << qUtf8Printable(data);
|
||||
clientConnection->transportClient()->sendData(frame.data);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_jsonRpcServer->processData(tunnelProxyClient, data);
|
||||
}
|
||||
// Unpack SLIP data, get address, pipe to client or give it to the json rpc server if address 0x0000
|
||||
|
||||
} else {
|
||||
// Not registered yet or doing other stuff...let the JSON RPC handle this data
|
||||
// Not registered yet or doing other stuff...let the JSON RPC server handle this data
|
||||
m_jsonRpcServer->processData(tunnelProxyClient, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -48,7 +48,10 @@ public:
|
||||
TunnelProxyErrorInvalidUuid,
|
||||
TunnelProxyErrorInternalServerError,
|
||||
TunnelProxyErrorServerNotFound,
|
||||
TunnelProxyErrorAlreadyRegistered
|
||||
TunnelProxyErrorForbiddenCall,
|
||||
TunnelProxyErrorAlreadyRegistered,
|
||||
TunnelProxyErrorNotRegistered,
|
||||
TunnelProxyErrorUnknownSocketAddress
|
||||
};
|
||||
Q_ENUM(TunnelProxyError)
|
||||
|
||||
@ -62,6 +65,7 @@ public:
|
||||
|
||||
TunnelProxyServer::TunnelProxyError registerServer(const QUuid &clientId, const QUuid &serverUuid, const QString &serverName);
|
||||
TunnelProxyServer::TunnelProxyError registerClient(const QUuid &clientId, const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid);
|
||||
TunnelProxyServer::TunnelProxyError disconnectClient(const QUuid &clientId, quint16 socketAddress);
|
||||
|
||||
public slots:
|
||||
void startServer();
|
||||
|
||||
@ -55,14 +55,44 @@ QString TunnelProxyServerConnection::serverName() const
|
||||
return m_serverName;
|
||||
}
|
||||
|
||||
QList<TunnelProxyClientConnection *> TunnelProxyServerConnection::clientConnections() const
|
||||
{
|
||||
return m_clientConnections.values();
|
||||
}
|
||||
|
||||
void TunnelProxyServerConnection::registerClientConnection(TunnelProxyClientConnection *clientConnection)
|
||||
{
|
||||
m_clientConnections.insert(clientConnection->clientUuid(), clientConnection);
|
||||
quint16 socketAddress = getFreeAddress();
|
||||
clientConnection->setSocketAddress(socketAddress);
|
||||
m_clientConnectionsAddresses.insert(socketAddress, clientConnection);
|
||||
}
|
||||
|
||||
void TunnelProxyServerConnection::unregisterClientConnection(TunnelProxyClientConnection *clientConnection)
|
||||
{
|
||||
m_clientConnections.remove(clientConnection->clientUuid());
|
||||
m_clientConnectionsAddresses.remove(clientConnection->socketAddress());
|
||||
}
|
||||
|
||||
TunnelProxyClientConnection *TunnelProxyServerConnection::getClientConnection(quint16 socketAddress)
|
||||
{
|
||||
return m_clientConnectionsAddresses.value(socketAddress);
|
||||
}
|
||||
|
||||
quint16 TunnelProxyServerConnection::getFreeAddress()
|
||||
{
|
||||
m_currentAddressCounter += 1;
|
||||
quint16 address = m_currentAddressCounter;
|
||||
for (int i = 0; i < m_connectionLimit; i++) {
|
||||
if (m_clientConnectionsAddresses.contains(address) || address == 0x0000 || address == 0xFFFF) {
|
||||
address++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Q_ASSERT_X(!m_clientConnectionsAddresses.contains(address), "TunnelProxyServerConnection", "no free address but the maximum of connections has been reached.");
|
||||
return address;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, TunnelProxyServerConnection *serverConnection)
|
||||
|
||||
@ -48,17 +48,27 @@ public:
|
||||
QUuid serverUuid() const;
|
||||
QString serverName() const;
|
||||
|
||||
QList<TunnelProxyClientConnection *> clientConnections() const;
|
||||
|
||||
void registerClientConnection(TunnelProxyClientConnection *clientConnection);
|
||||
void unregisterClientConnection(TunnelProxyClientConnection *clientConnection);
|
||||
|
||||
TunnelProxyClientConnection *getClientConnection(quint16 socketAddress);
|
||||
|
||||
signals:
|
||||
|
||||
private:
|
||||
TransportClient *m_transportClient = nullptr;
|
||||
QUuid m_serverUuid;
|
||||
QString m_serverName;
|
||||
quint16 m_connectionLimit = 100;
|
||||
|
||||
quint16 m_currentAddressCounter = 0;
|
||||
|
||||
QHash<QUuid, TunnelProxyClientConnection *> m_clientConnections;
|
||||
QHash<quint16, TunnelProxyClientConnection *> m_clientConnectionsAddresses;
|
||||
|
||||
quint16 getFreeAddress();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ TEMPLATE = lib
|
||||
TARGET = nymea-remoteproxyclient
|
||||
target.path = $$[QT_INSTALL_LIBS]
|
||||
|
||||
include(../common/common.pri)
|
||||
include(libnymea-remoteproxyclient.pri)
|
||||
|
||||
installheaders.files = remoteproxyconnection.h
|
||||
|
||||
@ -79,6 +79,20 @@ JsonReply *JsonRpcClient::callRegisterServer(const QUuid &serverUuid, const QStr
|
||||
return reply;
|
||||
}
|
||||
|
||||
JsonReply *JsonRpcClient::callRegisterClient(const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid)
|
||||
{
|
||||
QVariantMap params;
|
||||
params.insert("clientUuid", clientUuid);
|
||||
params.insert("clientName", clientName);
|
||||
params.insert("serverUuid", serverUuid.toString());
|
||||
|
||||
JsonReply *reply = new JsonReply(m_commandId, "TunnelProxy", "RegisterClient", params, this);
|
||||
qCDebug(dcRemoteProxyClientJsonRpc()) << "Calling" << QString("%1.%2").arg(reply->nameSpace()).arg(reply->method());
|
||||
sendRequest(reply->requestMap());
|
||||
m_replies.insert(m_commandId, reply);
|
||||
return reply;
|
||||
}
|
||||
|
||||
void JsonRpcClient::sendRequest(const QVariantMap &request)
|
||||
{
|
||||
QByteArray data = QJsonDocument::fromVariant(request).toJson(QJsonDocument::Compact);
|
||||
@ -97,6 +111,8 @@ void JsonRpcClient::processDataPackage(const QByteArray &data)
|
||||
|
||||
QVariantMap dataMap = jsonDoc.toVariant().toMap();
|
||||
|
||||
qCDebug(dcRemoteProxyClientJsonRpcTraffic()) << "Data received" << qUtf8Printable(data);
|
||||
|
||||
// check if this is a reply to a request
|
||||
int commandId = dataMap.value("id").toInt();
|
||||
JsonReply *reply = m_replies.take(commandId);
|
||||
@ -109,7 +125,7 @@ void JsonRpcClient::processDataPackage(const QByteArray &data)
|
||||
}
|
||||
|
||||
reply->setResponse(dataMap);
|
||||
reply->finished();
|
||||
emit reply->finished();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -126,6 +142,15 @@ void JsonRpcClient::processDataPackage(const QByteArray &data)
|
||||
QString clientName = notificationParams.value("name").toString();
|
||||
QString clientUuid = notificationParams.value("uuid").toString();
|
||||
emit tunnelEstablished(clientName, clientUuid);
|
||||
} else if (nameSpace == "TunnelProxy" && notificationName == "ClientConnected") {
|
||||
QString clientName = notificationParams.value("clientName").toString();
|
||||
QUuid clientUuid = notificationParams.value("clientUuid").toUuid();
|
||||
QString clientPeerAddress = notificationParams.value("clientPeerAddress").toString();
|
||||
quint16 socketAddress = static_cast<quint16>(notificationParams.value("socketAddress").toUInt());
|
||||
emit tunnelProxyClientConnected(clientName, clientUuid, clientPeerAddress, socketAddress);
|
||||
} else if (nameSpace == "TunnelProxy" && notificationName == "ClientDisconnected") {
|
||||
quint16 socketAddress = static_cast<quint16>(notificationParams.value("socketAddress").toUInt());
|
||||
emit tunnelProxyClientDisonnected(socketAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +55,8 @@ public:
|
||||
|
||||
// Tunnel proxy
|
||||
JsonReply *callRegisterServer(const QUuid &serverUuid, const QString &serverName);
|
||||
JsonReply *callRegisterClient(const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid);
|
||||
JsonReply *callDisconnectClient(quint16 socketAddress);
|
||||
|
||||
private:
|
||||
ProxyConnection *m_connection = nullptr;
|
||||
@ -69,6 +71,8 @@ private:
|
||||
|
||||
signals:
|
||||
void tunnelEstablished(const QString clientName, const QString &clientUuid);
|
||||
void tunnelProxyClientConnected(const QString &clientName, const QUuid &clientUuid, const QString &clientPeerAddress, quint16 socketAddress);
|
||||
void tunnelProxyClientDisonnected(quint16 socketAddress);
|
||||
|
||||
public slots:
|
||||
void processData(const QByteArray &data);
|
||||
|
||||
@ -1,6 +1,298 @@
|
||||
#include "tunnelproxyremoteconnection.h"
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
|
||||
* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with this project.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under contact@nymea.io
|
||||
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
TunnelProxyRemoteConnection::TunnelProxyRemoteConnection(QObject *parent) : QObject(parent)
|
||||
#include "tunnelproxyremoteconnection.h"
|
||||
#include "proxyconnection.h"
|
||||
#include "tcpsocketconnection.h"
|
||||
#include "websocketconnection.h"
|
||||
#include "proxyjsonrpcclient.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(dcTunnelProxyRemoteConnection, "TunnelProxyRemoteConnection")
|
||||
|
||||
namespace remoteproxyclient {
|
||||
|
||||
TunnelProxyRemoteConnection::TunnelProxyRemoteConnection(const QUuid &clientUuid, const QString &clientName, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_clientUuid(clientUuid),
|
||||
m_clientName(clientName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TunnelProxyRemoteConnection::TunnelProxyRemoteConnection(const QUuid &clientUuid, const QString &clientName, ConnectionType connectionType, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_clientUuid(clientUuid),
|
||||
m_clientName(clientName),
|
||||
m_connectionType(connectionType)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TunnelProxyRemoteConnection::~TunnelProxyRemoteConnection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool TunnelProxyRemoteConnection::remoteConnected() const
|
||||
{
|
||||
return m_remoteConnected;
|
||||
}
|
||||
|
||||
QAbstractSocket::SocketError TunnelProxyRemoteConnection::error() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::ignoreSslErrors()
|
||||
{
|
||||
m_connection->ignoreSslErrors();
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
m_connection->ignoreSslErrors(errors);
|
||||
}
|
||||
|
||||
QString TunnelProxyRemoteConnection::remoteProxyServer() const
|
||||
{
|
||||
return m_remoteProxyServer;
|
||||
}
|
||||
|
||||
QString TunnelProxyRemoteConnection::remoteProxyServerName() const
|
||||
{
|
||||
return m_remoteProxyServerName;
|
||||
}
|
||||
|
||||
QString TunnelProxyRemoteConnection::remoteProxyServerVersion() const
|
||||
{
|
||||
return m_remoteProxyServerVersion;
|
||||
}
|
||||
|
||||
QString TunnelProxyRemoteConnection::remoteProxyApiVersion() const
|
||||
{
|
||||
return m_remoteProxyApiVersion;
|
||||
}
|
||||
|
||||
bool TunnelProxyRemoteConnection::connectServer(const QUrl &url, const QUuid &serverUuid)
|
||||
{
|
||||
m_serverUrl = url;
|
||||
m_serverUuid = serverUuid;
|
||||
m_error = QAbstractSocket::UnknownSocketError;
|
||||
|
||||
cleanUp();
|
||||
|
||||
switch (m_connectionType) {
|
||||
case ConnectionTypeWebSocket:
|
||||
qCDebug(dcTunnelProxyRemoteConnection()) << "Creating a web socket connection to" << url.toString();
|
||||
m_connection = qobject_cast<ProxyConnection *>(new WebSocketConnection(this));
|
||||
break;
|
||||
case ConnectionTypeTcpSocket:
|
||||
qCDebug(dcTunnelProxyRemoteConnection()) << "Creating a TCP socket connection to" << url.toString();
|
||||
m_connection = qobject_cast<ProxyConnection *>(new TcpSocketConnection(this));
|
||||
break;
|
||||
}
|
||||
|
||||
connect(m_connection, &ProxyConnection::connectedChanged, this, &TunnelProxyRemoteConnection::onConnectionChanged);
|
||||
connect(m_connection, &ProxyConnection::dataReceived, this, &TunnelProxyRemoteConnection::onConnectionDataAvailable);
|
||||
connect(m_connection, &ProxyConnection::errorOccured, this, &TunnelProxyRemoteConnection::onConnectionSocketError);
|
||||
connect(m_connection, &ProxyConnection::stateChanged, this, &TunnelProxyRemoteConnection::onConnectionStateChanged);
|
||||
connect(m_connection, &ProxyConnection::sslErrors, this, &TunnelProxyRemoteConnection::sslErrors);
|
||||
|
||||
m_jsonClient = new JsonRpcClient(m_connection, this);
|
||||
|
||||
qCDebug(dcTunnelProxyRemoteConnection()) << "Connecting to" << m_serverUrl.toString();
|
||||
m_connection->connectServer(m_serverUrl);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::disconnectServer()
|
||||
{
|
||||
if (m_connection) {
|
||||
qCDebug(dcTunnelProxyRemoteConnection()) << "Disconnecting from" << m_connection->serverUrl().toString();
|
||||
m_connection->disconnectServer();
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::onConnectionChanged(bool connected)
|
||||
{
|
||||
if (connected) {
|
||||
qCDebug(dcTunnelProxyRemoteConnection()) << "Connected to remote proxy server.";
|
||||
setState(StateConnected);
|
||||
setState(StateInitializing);
|
||||
JsonReply *reply = m_jsonClient->callHello();
|
||||
connect(reply, &JsonReply::finished, this, &TunnelProxyRemoteConnection::onHelloFinished);
|
||||
} else {
|
||||
qCDebug(dcTunnelProxyRemoteConnection()) << "Disconnected from remote proxy server.";
|
||||
setState(StateDisconnected);
|
||||
cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::onConnectionDataAvailable(const QByteArray &data)
|
||||
{
|
||||
if (m_state != StateRemoteConnected) {
|
||||
m_jsonClient->processData(data);
|
||||
return;
|
||||
}
|
||||
|
||||
emit dataReady(data);
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::onConnectionSocketError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
setError(error);
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::onConnectionStateChanged(QAbstractSocket::SocketState state)
|
||||
{
|
||||
// Process the actuall socket state
|
||||
switch (state) {
|
||||
case QAbstractSocket::UnconnectedState:
|
||||
setState(StateDisconnected);
|
||||
break;
|
||||
case QAbstractSocket::HostLookupState:
|
||||
setState(StateHostLookup);
|
||||
break;
|
||||
case QAbstractSocket::ConnectingState:
|
||||
setState(StateConnecting);
|
||||
break;
|
||||
case QAbstractSocket::ConnectedState:
|
||||
setState(StateConnected);
|
||||
break;
|
||||
case QAbstractSocket::ClosingState:
|
||||
setState(StateDiconnecting);
|
||||
break;
|
||||
case QAbstractSocket::BoundState:
|
||||
case QAbstractSocket::ListeningState:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::onHelloFinished()
|
||||
{
|
||||
JsonReply *reply = static_cast<JsonReply *>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
QVariantMap response = reply->response();
|
||||
qCDebug(dcTunnelProxyRemoteConnection()) << "Hello response ready" << reply->commandId() << response;
|
||||
|
||||
if (response.value("status").toString() != "success") {
|
||||
qCWarning(dcTunnelProxyRemoteConnection()) << "Could not get initial information from proxy server.";
|
||||
m_connection->disconnectServer();
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap responseParams = response.value("params").toMap();
|
||||
m_remoteProxyServer = responseParams.value("server").toString();
|
||||
m_remoteProxyServerName = responseParams.value("name").toString();
|
||||
m_remoteProxyServerVersion = responseParams.value("version").toString();
|
||||
m_remoteProxyApiVersion = responseParams.value("apiVersion").toString();
|
||||
|
||||
setState(StateRegister);
|
||||
|
||||
JsonReply *registerReply = m_jsonClient->callRegisterClient(m_clientUuid, m_clientName, m_serverUuid);
|
||||
connect(registerReply, &JsonReply::finished, this, &TunnelProxyRemoteConnection::onClientRegistrationFinished);
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::onClientRegistrationFinished()
|
||||
{
|
||||
JsonReply *reply = static_cast<JsonReply *>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
QVariantMap response = reply->response();
|
||||
qCDebug(dcTunnelProxyRemoteConnection()) << "RegisterClient response ready" << reply->commandId() << response;
|
||||
|
||||
if (response.value("status").toString() != "success") {
|
||||
qCWarning(dcTunnelProxyRemoteConnection()) << "JSON RPC error. Failed to register as tunnel client on the remote proxy server.";
|
||||
m_connection->disconnectServer();
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap responseParams = response.value("params").toMap();
|
||||
if (responseParams.value("tunnelProxyError").toString() != "TunnelProxyErrorNoError") {
|
||||
qCWarning(dcTunnelProxyRemoteConnection()) << "Failed to register as tunnel client on the remote proxy server:" << responseParams.value("tunnelProxyError").toString();
|
||||
m_connection->disconnectServer();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcTunnelProxyRemoteConnection()) << "Registered successfully as tunnel client on the remote proxy server.";
|
||||
setState(StateRemoteConnected);
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::setState(State state)
|
||||
{
|
||||
if (m_state == state)
|
||||
return;
|
||||
|
||||
m_state = state;
|
||||
qCDebug(dcTunnelProxyRemoteConnection()) << "State changed" << m_state;
|
||||
emit stateChanged(m_state);
|
||||
|
||||
setRemoteConnected(m_state == StateRemoteConnected);
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::setRemoteConnected(bool remoteConnected)
|
||||
{
|
||||
if (m_remoteConnected == remoteConnected)
|
||||
return;
|
||||
|
||||
m_remoteConnected = remoteConnected;
|
||||
emit remoteConnectedChanged(m_remoteConnected);
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::setError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
if (m_error == error)
|
||||
return;
|
||||
|
||||
m_error = error;
|
||||
qCWarning(dcTunnelProxyRemoteConnection()) << "Error occured" << m_error;
|
||||
emit errorOccured(m_error);
|
||||
}
|
||||
|
||||
void TunnelProxyRemoteConnection::cleanUp()
|
||||
{
|
||||
if (m_jsonClient) {
|
||||
m_jsonClient->deleteLater();
|
||||
m_jsonClient = nullptr;
|
||||
}
|
||||
|
||||
if (m_connection) {
|
||||
m_connection->deleteLater();
|
||||
m_connection = nullptr;
|
||||
}
|
||||
|
||||
m_remoteProxyServer.clear();
|
||||
m_remoteProxyServerName.clear();
|
||||
m_remoteProxyServerVersion.clear();
|
||||
m_remoteProxyApiVersion.clear();
|
||||
|
||||
setState(StateDisconnected);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,16 +1,139 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
|
||||
* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with this project.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under contact@nymea.io
|
||||
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef TUNNELPROXYREMOTECONNECTION_H
|
||||
#define TUNNELPROXYREMOTECONNECTION_H
|
||||
|
||||
#include <QUuid>
|
||||
#include <QObject>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "proxyconnection.h"
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxyRemoteConnection)
|
||||
|
||||
namespace remoteproxyclient {
|
||||
|
||||
class JsonRpcClient;
|
||||
|
||||
class TunnelProxyRemoteConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TunnelProxyRemoteConnection(QObject *parent = nullptr);
|
||||
|
||||
enum State {
|
||||
StateConnecting,
|
||||
StateHostLookup,
|
||||
StateConnected,
|
||||
StateInitializing,
|
||||
StateRegister,
|
||||
StateRemoteConnected,
|
||||
StateDiconnecting,
|
||||
StateDisconnected,
|
||||
};
|
||||
Q_ENUM(State)
|
||||
|
||||
enum ConnectionType {
|
||||
ConnectionTypeWebSocket,
|
||||
ConnectionTypeTcpSocket
|
||||
};
|
||||
Q_ENUM(ConnectionType)
|
||||
|
||||
explicit TunnelProxyRemoteConnection(const QUuid &clientUuid, const QString &clientName, QObject *parent = nullptr);
|
||||
explicit TunnelProxyRemoteConnection(const QUuid &clientUuid, const QString &clientName, ConnectionType connectionType, QObject *parent = nullptr);
|
||||
~TunnelProxyRemoteConnection();
|
||||
|
||||
bool remoteConnected() const;
|
||||
|
||||
QAbstractSocket::SocketError error() const;
|
||||
|
||||
void ignoreSslErrors();
|
||||
void ignoreSslErrors(const QList<QSslError> &errors);
|
||||
|
||||
QString remoteProxyServer() const;
|
||||
QString remoteProxyServerName() const;
|
||||
QString remoteProxyServerVersion() const;
|
||||
QString remoteProxyApiVersion() const;
|
||||
|
||||
public slots:
|
||||
bool connectServer(const QUrl &url, const QUuid &serverUuid);
|
||||
void disconnectServer();
|
||||
|
||||
signals:
|
||||
void stateChanged(TunnelProxyRemoteConnection::State state);
|
||||
void errorOccured(QAbstractSocket::SocketError error);
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
|
||||
void remoteConnectedChanged(bool remoteConnected);
|
||||
|
||||
void dataReady(const QByteArray &data);
|
||||
|
||||
private slots:
|
||||
void onConnectionChanged(bool connected);
|
||||
void onConnectionDataAvailable(const QByteArray &data);
|
||||
|
||||
void onConnectionSocketError(QAbstractSocket::SocketError error);
|
||||
void onConnectionStateChanged(QAbstractSocket::SocketState state);
|
||||
|
||||
// Initialization calls
|
||||
void onHelloFinished();
|
||||
void onClientRegistrationFinished();
|
||||
|
||||
private:
|
||||
// This server information
|
||||
QUuid m_clientUuid;
|
||||
QString m_clientName;
|
||||
ConnectionType m_connectionType = ConnectionTypeTcpSocket;
|
||||
|
||||
QUuid m_serverUuid;
|
||||
|
||||
bool m_remoteConnected = false;
|
||||
|
||||
// Remote proxy server information
|
||||
QString m_remoteProxyServer;
|
||||
QString m_remoteProxyServerName;
|
||||
QString m_remoteProxyServerVersion;
|
||||
QString m_remoteProxyApiVersion;
|
||||
|
||||
QUrl m_serverUrl;
|
||||
QAbstractSocket::SocketError m_error = QAbstractSocket::UnknownSocketError;
|
||||
State m_state = StateDisconnected;
|
||||
|
||||
ProxyConnection *m_connection = nullptr;
|
||||
JsonRpcClient *m_jsonClient = nullptr;
|
||||
|
||||
void setState(State state);
|
||||
void setRemoteConnected(bool remoteConnected);
|
||||
void setError(QAbstractSocket::SocketError error);
|
||||
|
||||
void cleanUp();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TUNNELPROXYREMOTECONNECTION_H
|
||||
|
||||
@ -26,12 +26,63 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "tunnelproxysocket.h"
|
||||
#include "proxyconnection.h"
|
||||
#include "../common/slipdataprocessor.h"
|
||||
|
||||
namespace remoteproxyclient {
|
||||
|
||||
TunnelProxySocket::TunnelProxySocket(QObject *parent) : QObject(parent)
|
||||
TunnelProxySocket::TunnelProxySocket(ProxyConnection *connection, const QString &clientName, const QUuid &clientUuid, const QHostAddress &clientPeerAddress, quint16 socketAddress, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_connection(connection),
|
||||
m_clientName(clientName),
|
||||
m_clientUuid(clientUuid),
|
||||
m_clientPeerAddress(clientPeerAddress),
|
||||
m_socketAddress(socketAddress)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QUuid TunnelProxySocket::clientUuid() const
|
||||
{
|
||||
return m_clientUuid;
|
||||
}
|
||||
|
||||
QString TunnelProxySocket::clientName() const
|
||||
{
|
||||
return m_clientName;
|
||||
}
|
||||
|
||||
QHostAddress TunnelProxySocket::clientPeerAddress() const
|
||||
{
|
||||
return m_clientPeerAddress;
|
||||
}
|
||||
|
||||
quint16 TunnelProxySocket::socketAddress() const
|
||||
{
|
||||
return m_socketAddress;
|
||||
}
|
||||
|
||||
void TunnelProxySocket::writeData(const QByteArray &data)
|
||||
{
|
||||
SlipDataProcessor::Frame frame;
|
||||
frame.socketAddress = m_socketAddress;
|
||||
frame.data = data;
|
||||
m_connection->sendData(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
|
||||
}
|
||||
|
||||
void TunnelProxySocket::disconnectSocket()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, TunnelProxySocket *tunnelProxySocket)
|
||||
{
|
||||
debug.nospace() << "TunnelProxySocket(";
|
||||
debug.nospace() << tunnelProxySocket->clientName() << ", ";
|
||||
debug.nospace() << tunnelProxySocket->clientUuid().toString() << ", ";
|
||||
debug.nospace() << tunnelProxySocket->clientPeerAddress().toString() << ", ";
|
||||
debug.nospace() << tunnelProxySocket->socketAddress() << ")";
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -28,20 +28,48 @@
|
||||
#ifndef TUNNELPROXYSOCKET_H
|
||||
#define TUNNELPROXYSOCKET_H
|
||||
|
||||
#include <QUuid>
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
|
||||
namespace remoteproxyclient {
|
||||
|
||||
class ProxyConnection;
|
||||
|
||||
class TunnelProxySocket : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
friend class TunnelProxySocketServer;
|
||||
|
||||
public:
|
||||
explicit TunnelProxySocket(QObject *parent = nullptr);
|
||||
QUuid clientUuid() const;
|
||||
QString clientName() const;
|
||||
QHostAddress clientPeerAddress() const;
|
||||
quint16 socketAddress() const;
|
||||
|
||||
void writeData(const QByteArray &data);
|
||||
|
||||
void disconnectSocket();
|
||||
|
||||
signals:
|
||||
void dataReceived(const QByteArray &data);
|
||||
void disconnected();
|
||||
|
||||
private:
|
||||
explicit TunnelProxySocket(ProxyConnection *connection, const QString &clientName, const QUuid &clientUuid, const QHostAddress &clientPeerAddress, quint16 socketAddress, QObject *parent = nullptr);
|
||||
~TunnelProxySocket() = default;
|
||||
|
||||
ProxyConnection *m_connection = nullptr;
|
||||
|
||||
QString m_clientName;
|
||||
QUuid m_clientUuid;
|
||||
QHostAddress m_clientPeerAddress;
|
||||
quint16 m_socketAddress = 0xFFFF;
|
||||
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, TunnelProxySocket *tunnelProxySocket);
|
||||
|
||||
}
|
||||
|
||||
#endif // TUNNELPROXYSOCKET_H
|
||||
|
||||
@ -30,8 +30,10 @@
|
||||
#include "tcpsocketconnection.h"
|
||||
#include "websocketconnection.h"
|
||||
#include "proxyjsonrpcclient.h"
|
||||
#include "../common/slipdataprocessor.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(dcTunnelProxySocketServer, "dcTunnelProxySocketServer")
|
||||
Q_LOGGING_CATEGORY(dcTunnelProxySocketServerTraffic, "dcTunnelProxySocketServerTraffic")
|
||||
|
||||
namespace remoteproxyclient {
|
||||
|
||||
@ -67,10 +69,14 @@ QAbstractSocket::SocketError TunnelProxySocketServer::error() const
|
||||
return m_error;
|
||||
}
|
||||
|
||||
TunnelProxySocketServer::Error TunnelProxySocketServer::serverError() const
|
||||
{
|
||||
return m_serverError;
|
||||
}
|
||||
|
||||
void TunnelProxySocketServer::ignoreSslErrors()
|
||||
{
|
||||
m_connection->ignoreSslErrors();
|
||||
|
||||
}
|
||||
|
||||
void TunnelProxySocketServer::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
@ -78,6 +84,32 @@ void TunnelProxySocketServer::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
m_connection->ignoreSslErrors(errors);
|
||||
}
|
||||
|
||||
QUrl TunnelProxySocketServer::serverUrl() const
|
||||
{
|
||||
return m_serverUrl;
|
||||
}
|
||||
|
||||
QString TunnelProxySocketServer::remoteProxyServer() const
|
||||
{
|
||||
return m_remoteProxyServer;
|
||||
}
|
||||
|
||||
QString TunnelProxySocketServer::remoteProxyServerName() const
|
||||
{
|
||||
return m_remoteProxyServerName;
|
||||
}
|
||||
|
||||
QString TunnelProxySocketServer::remoteProxyServerVersion() const
|
||||
{
|
||||
return m_remoteProxyServerVersion;
|
||||
}
|
||||
|
||||
QString TunnelProxySocketServer::remoteProxyApiVersion() const
|
||||
{
|
||||
return m_remoteProxyApiVersion;
|
||||
}
|
||||
|
||||
|
||||
void TunnelProxySocketServer::startServer(const QUrl &serverUrl)
|
||||
{
|
||||
m_serverUrl = serverUrl;
|
||||
@ -103,7 +135,8 @@ void TunnelProxySocketServer::startServer(const QUrl &serverUrl)
|
||||
connect(m_connection, &ProxyConnection::sslErrors, this, &TunnelProxySocketServer::sslErrors);
|
||||
|
||||
m_jsonClient = new JsonRpcClient(m_connection, this);
|
||||
// connect(m_jsonClient, &JsonRpcClient::tunnelEstablished, this, &RemoteProxyConnection::onTunnelEstablished);
|
||||
connect(m_jsonClient, &JsonRpcClient::tunnelProxyClientConnected, this, &TunnelProxySocketServer::onTunnelProxyClientConnected);
|
||||
connect(m_jsonClient, &JsonRpcClient::tunnelProxyClientDisonnected, this, &TunnelProxySocketServer::onTunnelProxyClientDisconnected);
|
||||
|
||||
qCDebug(dcTunnelProxySocketServer()) << "Connecting to" << m_serverUrl.toString();
|
||||
m_connection->connectServer(m_serverUrl);
|
||||
@ -111,7 +144,10 @@ void TunnelProxySocketServer::startServer(const QUrl &serverUrl)
|
||||
|
||||
void TunnelProxySocketServer::stopServer()
|
||||
{
|
||||
// Close the server connection and all related sockets
|
||||
if (m_connection) {
|
||||
qCDebug(dcTunnelProxySocketServer()) << "Disconnecting from" << m_connection->serverUrl().toString();
|
||||
m_connection->disconnectServer();
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelProxySocketServer::onConnectionChanged(bool connected)
|
||||
@ -119,6 +155,7 @@ void TunnelProxySocketServer::onConnectionChanged(bool connected)
|
||||
if (connected) {
|
||||
qCDebug(dcTunnelProxySocketServer()) << "Connected to remote proxy server.";
|
||||
setState(StateConnected);
|
||||
m_serverError = TunnelProxySocketServer::ErrorNoError;
|
||||
|
||||
setState(StateInitializing);
|
||||
JsonReply *reply = m_jsonClient->callHello();
|
||||
@ -126,18 +163,53 @@ void TunnelProxySocketServer::onConnectionChanged(bool connected)
|
||||
} else {
|
||||
qCDebug(dcTunnelProxySocketServer()) << "Disconnected from remote proxy server.";
|
||||
setState(StateDisconnected);
|
||||
setServerError(ErrorNotConnected);
|
||||
cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelProxySocketServer::onConnectionDataAvailable(const QByteArray &data)
|
||||
{
|
||||
qCDebug(dcTunnelProxySocketServerTraffic()) << "Data received" << qUtf8Printable(data);
|
||||
|
||||
if (m_state != StateRunning) {
|
||||
m_jsonClient->processData(data);
|
||||
m_dataBuffer.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse SLIP
|
||||
// Parse SLIP frame
|
||||
for (int i = 0; i < data.length(); i++) {
|
||||
quint8 byte = static_cast<quint8>(data.at(i));
|
||||
if (byte == SlipDataProcessor::ProtocolByteEnd) {
|
||||
// If there is no data...continue since it might be a starting END byte
|
||||
if (m_dataBuffer.isEmpty())
|
||||
continue;
|
||||
|
||||
QByteArray frameData = SlipDataProcessor::deserializeData(m_dataBuffer);
|
||||
if (frameData.isNull()) {
|
||||
qCWarning(dcTunnelProxySocketServerTraffic()) << "Received inconsistant SLIP encoded message. Ignoring data...";
|
||||
} else {
|
||||
SlipDataProcessor::Frame frame = SlipDataProcessor::parseFrame(frameData);
|
||||
qCDebug(dcTunnelProxySocketServerTraffic()) << "Frame received" << frame.socketAddress << qUtf8Printable(frame.data);
|
||||
if (frame.socketAddress == 0x0000) {
|
||||
m_jsonClient->processData(frame.data);
|
||||
} else {
|
||||
// Find the socket and emit the data received signal
|
||||
TunnelProxySocket *tunnlProxySocket = m_tunnelProxySockets.value(frame.socketAddress);
|
||||
if (!tunnlProxySocket) {
|
||||
qCWarning(dcTunnelProxySocketServer()) << "Received data from unknown tunnel proxy client with address" << frame.socketAddress << "...ignoring the data";
|
||||
} else {
|
||||
emit tunnlProxySocket->dataReceived(frame.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_dataBuffer.clear();
|
||||
} else {
|
||||
m_dataBuffer.append(data.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelProxySocketServer::onConnectionSocketError(QAbstractSocket::SocketError error)
|
||||
@ -181,6 +253,7 @@ void TunnelProxySocketServer::onHelloFinished()
|
||||
if (response.value("status").toString() != "success") {
|
||||
qCWarning(dcTunnelProxySocketServer()) << "Could not get initial information from proxy server.";
|
||||
m_connection->disconnectServer();
|
||||
setServerError(ErrorConnectionError);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -207,6 +280,7 @@ void TunnelProxySocketServer::onServerRegistrationFinished()
|
||||
if (response.value("status").toString() != "success") {
|
||||
qCWarning(dcTunnelProxySocketServer()) << "JSON RPC error. Failed to register as tunnel server on the remote proxy server.";
|
||||
m_connection->disconnectServer();
|
||||
setServerError(ErrorConnectionError);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -214,11 +288,34 @@ void TunnelProxySocketServer::onServerRegistrationFinished()
|
||||
if (responseParams.value("tunnelProxyError").toString() != "TunnelProxyErrorNoError") {
|
||||
qCWarning(dcTunnelProxySocketServer()) << "Failed to register as tunnel server on the remote proxy server:" << responseParams.value("tunnelProxyError").toString();
|
||||
m_connection->disconnectServer();
|
||||
setServerError(ErrorConnectionError);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcTunnelProxySocketServer()) << "Registered successfully as tunnel server on the remote proxy server.";
|
||||
setState(StateRunning);
|
||||
m_serverError = ErrorNoError;
|
||||
}
|
||||
|
||||
void TunnelProxySocketServer::onTunnelProxyClientConnected(const QString &clientName, const QUuid &clientUuid, const QString &clientPeerAddress, quint16 socketAddress)
|
||||
{
|
||||
TunnelProxySocket *tunnelProxySocket = new TunnelProxySocket(m_connection, clientName, clientUuid, QHostAddress(clientPeerAddress), socketAddress, this);
|
||||
qCDebug(dcTunnelProxySocketServer()) << "--> New client connected" << tunnelProxySocket;
|
||||
m_tunnelProxySockets.insert(socketAddress, tunnelProxySocket);
|
||||
emit clientConnected(tunnelProxySocket);
|
||||
}
|
||||
|
||||
void TunnelProxySocketServer::onTunnelProxyClientDisconnected(quint16 socketAddress)
|
||||
{
|
||||
TunnelProxySocket *tunnelProxySocket = m_tunnelProxySockets.take(socketAddress);
|
||||
if (!tunnelProxySocket) {
|
||||
qCWarning(dcTunnelProxySocketServer()) << "Unknown client disconnected" << socketAddress << ". Ignoring the event.";
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcTunnelProxySocketServer()) << "--> Client disconnected" << tunnelProxySocket;
|
||||
emit tunnelProxySocket->disconnected();
|
||||
tunnelProxySocket->deleteLater();
|
||||
}
|
||||
|
||||
void TunnelProxySocketServer::setState(State state)
|
||||
@ -253,10 +350,21 @@ void TunnelProxySocketServer::setError(QAbstractSocket::SocketError error)
|
||||
emit errorOccured(m_error);
|
||||
}
|
||||
|
||||
void TunnelProxySocketServer::setServerError(Error error)
|
||||
{
|
||||
if (m_serverError == error)
|
||||
return;
|
||||
|
||||
qCDebug(dcTunnelProxySocketServer()) << "Server error occured" << error;
|
||||
m_serverError = error;
|
||||
emit serverErrorOccured(m_serverError);
|
||||
}
|
||||
|
||||
void TunnelProxySocketServer::cleanUp()
|
||||
{
|
||||
// TODO: cleanup client connections
|
||||
|
||||
foreach (quint16 socketAddress, m_tunnelProxySockets.keys()) {
|
||||
onTunnelProxyClientDisconnected(socketAddress);
|
||||
}
|
||||
|
||||
if (m_jsonClient) {
|
||||
m_jsonClient->deleteLater();
|
||||
@ -268,8 +376,10 @@ void TunnelProxySocketServer::cleanUp()
|
||||
m_connection = nullptr;
|
||||
}
|
||||
|
||||
// m_serverName = QString();
|
||||
// m_serverUuid = QUuid();
|
||||
m_remoteProxyServer.clear();
|
||||
m_remoteProxyServerName.clear();
|
||||
m_remoteProxyServerVersion.clear();
|
||||
m_remoteProxyApiVersion.clear();
|
||||
|
||||
setState(StateDisconnected);
|
||||
}
|
||||
|
||||
@ -32,14 +32,14 @@
|
||||
#include <QObject>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxySocketServer)
|
||||
|
||||
#include "proxyconnection.h"
|
||||
#include "tunnelproxysocket.h"
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxySocketServer)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxySocketServerTraffic)
|
||||
|
||||
namespace remoteproxyclient {
|
||||
|
||||
class ProxyConnection;
|
||||
class JsonRpcClient;
|
||||
|
||||
class TunnelProxySocketServer : public QObject
|
||||
@ -64,6 +64,15 @@ public:
|
||||
};
|
||||
Q_ENUM(ConnectionType)
|
||||
|
||||
enum Error {
|
||||
ErrorNoError,
|
||||
ErrorConnectionError,
|
||||
ErrorInvalidAddress,
|
||||
ErrorNotConnected,
|
||||
ErrorNotRegistered
|
||||
};
|
||||
Q_ENUM(Error)
|
||||
|
||||
explicit TunnelProxySocketServer(const QUuid &serverUuid, const QString &serverName, QObject *parent = nullptr);
|
||||
explicit TunnelProxySocketServer(const QUuid &serverUuid, const QString &serverName, ConnectionType connectionType, QObject *parent = nullptr);
|
||||
~TunnelProxySocketServer();
|
||||
@ -71,21 +80,32 @@ public:
|
||||
bool running() const;
|
||||
|
||||
QAbstractSocket::SocketError error() const;
|
||||
Error serverError() const;
|
||||
|
||||
void ignoreSslErrors();
|
||||
void ignoreSslErrors(const QList<QSslError> &errors);
|
||||
|
||||
QUrl serverUrl() const;
|
||||
|
||||
QString remoteProxyServer() const;
|
||||
QString remoteProxyServerName() const;
|
||||
QString remoteProxyServerVersion() const;
|
||||
QString remoteProxyApiVersion() const;
|
||||
|
||||
public slots:
|
||||
void startServer(const QUrl &serverUrl);
|
||||
void stopServer();
|
||||
|
||||
signals:
|
||||
void runningChanged(bool running);
|
||||
|
||||
void stateChanged(TunnelProxySocketServer::State state);
|
||||
void errorOccured(QAbstractSocket::SocketError error);
|
||||
void serverErrorOccured(Error error);
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
|
||||
void runningChanged(bool running);
|
||||
|
||||
void clientConnected(TunnelProxySocket *tunnelProxySocket);
|
||||
void clientDisconnected(TunnelProxySocket *tunnelProxySocket);
|
||||
|
||||
private slots:
|
||||
void onConnectionChanged(bool connected);
|
||||
@ -98,6 +118,10 @@ private slots:
|
||||
void onHelloFinished();
|
||||
void onServerRegistrationFinished();
|
||||
|
||||
// Client notifications
|
||||
void onTunnelProxyClientConnected(const QString &clientName, const QUuid &clientUuid, const QString &clientPeerAddress, quint16 socketAddress);
|
||||
void onTunnelProxyClientDisconnected(quint16 socketAddress);
|
||||
|
||||
private:
|
||||
// This server information
|
||||
QUuid m_serverUuid;
|
||||
@ -113,14 +137,20 @@ private:
|
||||
bool m_running = false;
|
||||
QUrl m_serverUrl;
|
||||
QAbstractSocket::SocketError m_error = QAbstractSocket::UnknownSocketError;
|
||||
Error m_serverError = ErrorNoError;
|
||||
State m_state = StateDisconnected;
|
||||
|
||||
ProxyConnection *m_connection = nullptr;
|
||||
JsonRpcClient *m_jsonClient = nullptr;
|
||||
|
||||
QHash<quint16, TunnelProxySocket *> m_tunnelProxySockets;
|
||||
|
||||
QByteArray m_dataBuffer;
|
||||
|
||||
void setState(State state);
|
||||
void setRunning(bool running);
|
||||
void setError(QAbstractSocket::SocketError error);
|
||||
void setServerError(Error error);
|
||||
|
||||
void cleanUp();
|
||||
};
|
||||
|
||||
@ -4,11 +4,11 @@ TEMPLATE=subdirs
|
||||
SUBDIRS += server client libnymea-remoteproxy libnymea-remoteproxyclient
|
||||
|
||||
!disabletests {
|
||||
SUBDIRS+=tests
|
||||
SUBDIRS += tests
|
||||
}
|
||||
|
||||
!disablemonitor {
|
||||
SUBDIRS+=monitor
|
||||
SUBDIRS += monitor
|
||||
}
|
||||
|
||||
server.depends = libnymea-remoteproxy
|
||||
@ -26,8 +26,14 @@ message("----------------------------------------------------------")
|
||||
message("JSON-RPC API version $${API_VERSION_MAJOR}.$${API_VERSION_MINOR}")
|
||||
message("Qt version:" $$[QT_VERSION])
|
||||
|
||||
coverage { message("Building with coverage report") }
|
||||
ccache { message("Building with ccache support") }
|
||||
coverage {
|
||||
message("Building with coverage report")
|
||||
}
|
||||
|
||||
ccache {
|
||||
message("Building with ccache support")
|
||||
}
|
||||
|
||||
disablemonitor {
|
||||
message("Building without the monitor")
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
|
||||
// Client
|
||||
#include "tunnelproxy/tunnelproxysocketserver.h"
|
||||
#include "tunnelproxy/tunnelproxyremoteconnection.h"
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QSignalSpy>
|
||||
@ -296,6 +297,8 @@ void RemoteProxyTestsTunnelProxy::registerClient()
|
||||
QVERIFY(!response.isEmpty());
|
||||
QVERIFY(response.value("status").toString() == "success");
|
||||
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
|
||||
QVERIFY(response.value("params").toMap().contains("slipEnabled"));
|
||||
//QVERIFY(response.value("params").toMap().value("slipEnabled").toBool()));
|
||||
verifyTunnelProxyError(response, expectedServerError);
|
||||
}
|
||||
|
||||
@ -358,27 +361,24 @@ void RemoteProxyTestsTunnelProxy::registerServerDuplicated()
|
||||
QVERIFY(!response.isEmpty());
|
||||
QVERIFY(response.value("status").toString() == "success");
|
||||
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
|
||||
QVERIFY(response.value("params").toMap().contains("slipEnabled"));
|
||||
QVERIFY(response.value("params").toMap().value("slipEnabled").toBool());
|
||||
verifyTunnelProxyError(response);
|
||||
|
||||
// SLIP Should be enabled now
|
||||
|
||||
// Try to register again with the same uuid on the same socket
|
||||
result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, socket);
|
||||
result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, true, socket);
|
||||
response = result.first.toMap();
|
||||
QVERIFY(response.value("status").toString() == "success");
|
||||
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
|
||||
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered);
|
||||
|
||||
// Try to register an invalid server uuid
|
||||
QVariantMap paramsInvalidUuid;
|
||||
paramsInvalidUuid.insert("serverName", serverName);
|
||||
paramsInvalidUuid.insert("serverUuid", QUuid().toString());
|
||||
result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", paramsInvalidUuid, socket);
|
||||
response = result.first.toMap();
|
||||
QVERIFY(response.value("status").toString() == "success");
|
||||
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
|
||||
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorInvalidUuid);
|
||||
QSignalSpy disconnectedTcpSpy(socket, &QSslSocket::disconnected);
|
||||
QVERIFY(disconnectedTcpSpy.wait());
|
||||
QVERIFY(disconnectedTcpSpy.count() == 1);
|
||||
|
||||
// Close the tcp socket
|
||||
socket->close();
|
||||
delete socket;
|
||||
|
||||
QTest::qWait(100);
|
||||
@ -394,18 +394,16 @@ void RemoteProxyTestsTunnelProxy::registerServerDuplicated()
|
||||
verifyTunnelProxyError(response);
|
||||
|
||||
// Try to register again with the same uuid on the same socket
|
||||
resultWebSocket = invokeWebSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, webSocket);
|
||||
resultWebSocket = invokeWebSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, true, webSocket);
|
||||
response = resultWebSocket.first.toMap();
|
||||
QVERIFY(response.value("status").toString() == "success");
|
||||
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
|
||||
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered);
|
||||
|
||||
// Try to register an invalid server uuid
|
||||
resultWebSocket = invokeWebSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", paramsInvalidUuid, webSocket);
|
||||
response = resultWebSocket.first.toMap();
|
||||
QVERIFY(response.value("status").toString() == "success");
|
||||
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
|
||||
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorInvalidUuid);
|
||||
|
||||
QSignalSpy disconnectedWebSocketSpy(webSocket, &QWebSocket::disconnected);
|
||||
QVERIFY(disconnectedWebSocketSpy.wait());
|
||||
QVERIFY(disconnectedWebSocketSpy.count() == 1);
|
||||
|
||||
webSocket->close();
|
||||
delete webSocket;
|
||||
@ -470,7 +468,6 @@ void RemoteProxyTestsTunnelProxy::registerClientDuplicated()
|
||||
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
|
||||
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered);
|
||||
|
||||
|
||||
// CleanUp
|
||||
if (clientSocketTcp) {
|
||||
// Close the tcp socket
|
||||
@ -501,8 +498,10 @@ void RemoteProxyTestsTunnelProxy::crossRegisterServerClient()
|
||||
// Start the server
|
||||
startServer();
|
||||
|
||||
resetDebugCategories();
|
||||
resetDebugCategories();
|
||||
addDebugCategory("TunnelProxyServer.debug=true");
|
||||
addDebugCategory("TunnelProxyServerTraffic.debug=true");
|
||||
addDebugCategory("JsonRpcTraffic.debug=true");
|
||||
|
||||
// Create the server and keep it up
|
||||
@ -512,7 +511,7 @@ void RemoteProxyTestsTunnelProxy::crossRegisterServerClient()
|
||||
serverParams.insert("serverName", serverName);
|
||||
serverParams.insert("serverUuid", serverUuid.toString());
|
||||
|
||||
// TCP socket
|
||||
// TCP server
|
||||
QPair<QVariant, QSslSocket *> serverResult = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", serverParams);
|
||||
QVariantMap response = serverResult.first.toMap();
|
||||
QSslSocket *serverSocket = serverResult.second;
|
||||
@ -521,6 +520,7 @@ void RemoteProxyTestsTunnelProxy::crossRegisterServerClient()
|
||||
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
|
||||
verifyTunnelProxyError(response);
|
||||
|
||||
// SLIP is enabled now
|
||||
|
||||
// Now try to register as client
|
||||
QVariantMap clientParams;
|
||||
@ -528,13 +528,17 @@ void RemoteProxyTestsTunnelProxy::crossRegisterServerClient()
|
||||
clientParams.insert("clientUuid", QUuid::createUuid().toString());
|
||||
clientParams.insert("serverUuid", serverUuid.toString());
|
||||
|
||||
QPair<QVariant, QSslSocket *> result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterClient", clientParams, serverSocket);
|
||||
QPair<QVariant, QSslSocket *> result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterClient", clientParams, true, serverSocket);
|
||||
response = result.first.toMap();
|
||||
QVERIFY(!response.isEmpty());
|
||||
QVERIFY(response.value("status").toString() == "success");
|
||||
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
|
||||
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered);
|
||||
|
||||
QSignalSpy disconnectedSpy(serverSocket, &QSslSocket::disconnected);
|
||||
QVERIFY(disconnectedSpy.wait());
|
||||
QVERIFY(disconnectedSpy.count() == 1);
|
||||
|
||||
serverSocket->close();
|
||||
delete serverSocket;
|
||||
|
||||
@ -551,8 +555,79 @@ void RemoteProxyTestsTunnelProxy::testTunnelProxyServer()
|
||||
|
||||
resetDebugCategories();
|
||||
addDebugCategory("TunnelProxyServer.debug=true");
|
||||
addDebugCategory("TunnelProxyServerTraffic.debug=true");
|
||||
addDebugCategory("JsonRpcTraffic.debug=true");
|
||||
addDebugCategory("TunnelProxySocketServer.debug=true");
|
||||
addDebugCategory("TunnelProxyRemoteConnection.debug=true");
|
||||
addDebugCategory("RemoteProxyClientJsonRpcTraffic.debug=true");
|
||||
|
||||
// Tunnel proxy socket server
|
||||
QString serverName = "SuperDuper server name";
|
||||
QUuid serverUuid = QUuid::createUuid();
|
||||
|
||||
TunnelProxySocketServer *tunnelProxyServer = new TunnelProxySocketServer(serverUuid, serverName, this);
|
||||
connect(tunnelProxyServer, &TunnelProxySocketServer::sslErrors, this, [=](const QList<QSslError> &errors){
|
||||
tunnelProxyServer->ignoreSslErrors(errors);
|
||||
});
|
||||
|
||||
tunnelProxyServer->startServer(m_serverUrlTunnelProxyTcp);
|
||||
|
||||
QSignalSpy serverRunningSpy(tunnelProxyServer, &TunnelProxySocketServer::runningChanged);
|
||||
serverRunningSpy.wait();
|
||||
QVERIFY(serverRunningSpy.count() == 1);
|
||||
QList<QVariant> arguments = serverRunningSpy.takeFirst();
|
||||
QVERIFY(arguments.at(0).toBool() == true);
|
||||
QVERIFY(tunnelProxyServer->running());
|
||||
|
||||
// Tunnel proxy client connection
|
||||
QString clientName = "Awesome client name";
|
||||
QUuid clientUuid = QUuid::createUuid();
|
||||
|
||||
TunnelProxyRemoteConnection *clientConnection = new TunnelProxyRemoteConnection(clientUuid, clientName, this);
|
||||
connect(clientConnection, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList<QSslError> &errors){
|
||||
clientConnection->ignoreSslErrors(errors);
|
||||
});
|
||||
|
||||
clientConnection->connectServer(m_serverUrlTunnelProxyTcp, serverUuid);
|
||||
|
||||
QSignalSpy clientConnectedSpy(tunnelProxyServer, &TunnelProxySocketServer::clientConnected);
|
||||
QVERIFY(clientConnectedSpy.wait());
|
||||
QVERIFY(clientConnectedSpy.count() == 1);
|
||||
arguments = clientConnectedSpy.takeFirst();
|
||||
TunnelProxySocket *tunnelProxySocket = arguments.at(0).value<TunnelProxySocket *>();
|
||||
QVERIFY(tunnelProxySocket->clientName() == clientName);
|
||||
QVERIFY(tunnelProxySocket->clientUuid() == clientUuid);
|
||||
|
||||
// Stop the server connection and verify the client gets disconnected
|
||||
|
||||
tunnelProxyServer->stopServer();
|
||||
|
||||
QSignalSpy disconnectedSpy(clientConnection, &TunnelProxyRemoteConnection::remoteConnectedChanged);
|
||||
QVERIFY(disconnectedSpy.wait());
|
||||
QVERIFY(disconnectedSpy.count() == 1);
|
||||
arguments = disconnectedSpy.takeFirst();
|
||||
QVERIFY(arguments.at(0).toBool() == false);
|
||||
QVERIFY(!clientConnection->remoteConnected());
|
||||
|
||||
|
||||
resetDebugCategories();
|
||||
|
||||
// Clean up
|
||||
stopServer();
|
||||
}
|
||||
|
||||
void RemoteProxyTestsTunnelProxy::testTunnelProxyClient()
|
||||
{
|
||||
// Start the server
|
||||
startServer();
|
||||
|
||||
resetDebugCategories();
|
||||
addDebugCategory("TunnelProxyServer.debug=true");
|
||||
addDebugCategory("TunnelProxyServerTraffic.debug=true");
|
||||
addDebugCategory("JsonRpcTraffic.debug=true");
|
||||
addDebugCategory("TunnelProxySocketServer.debug=true");
|
||||
addDebugCategory("TunnelProxyRemoteConnection.debug=true");
|
||||
addDebugCategory("RemoteProxyClientJsonRpcTraffic.debug=true");
|
||||
|
||||
// Tunnel proxy socket server
|
||||
QString serverName = "SuperDuper server name";
|
||||
@ -574,6 +649,24 @@ void RemoteProxyTestsTunnelProxy::testTunnelProxyServer()
|
||||
|
||||
|
||||
// Tunnel proxy client connection
|
||||
QString clientName = "Awesome client name";
|
||||
QUuid clientUuid = QUuid::createUuid();
|
||||
|
||||
TunnelProxyRemoteConnection *clientConnection = new TunnelProxyRemoteConnection(clientUuid, clientName, this);
|
||||
connect(clientConnection, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList<QSslError> &errors){
|
||||
clientConnection->ignoreSslErrors(errors);
|
||||
});
|
||||
|
||||
clientConnection->connectServer(m_serverUrlTunnelProxyTcp, serverUuid);
|
||||
|
||||
QSignalSpy clientRemoteConnectedSpy(clientConnection, &TunnelProxyRemoteConnection::remoteConnectedChanged);
|
||||
QVERIFY(clientRemoteConnectedSpy.wait());
|
||||
QVERIFY(clientRemoteConnectedSpy.count() == 1);
|
||||
arguments = clientRemoteConnectedSpy.takeFirst();
|
||||
QVERIFY(arguments.at(0).toBool() == true);
|
||||
QVERIFY(clientConnection->remoteConnected());
|
||||
|
||||
// Verify server socket connected
|
||||
|
||||
|
||||
|
||||
|
||||
@ -66,6 +66,7 @@ private slots:
|
||||
|
||||
// Client classes
|
||||
void testTunnelProxyServer();
|
||||
void testTunnelProxyClient();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -31,6 +31,8 @@
|
||||
#include "loggingcategories.h"
|
||||
#include "remoteproxyconnection.h"
|
||||
|
||||
#include "../common/slipdataprocessor.h"
|
||||
|
||||
#include <QSslError>
|
||||
#include <QMetaType>
|
||||
#include <QSignalSpy>
|
||||
@ -656,7 +658,7 @@ QVariant BaseTest::injectTcpSocketTunnelProxyData(const QByteArray &data)
|
||||
return jsonDoc.toVariant();
|
||||
}
|
||||
|
||||
QPair<QVariant, QSslSocket *> BaseTest::invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params, QSslSocket *existingSocket)
|
||||
QPair<QVariant, QSslSocket *> BaseTest::invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params, bool slipEnabled, QSslSocket *existingSocket)
|
||||
{
|
||||
QSslSocket *socket = nullptr;
|
||||
if (!existingSocket) {
|
||||
@ -680,30 +682,66 @@ QPair<QVariant, QSslSocket *> BaseTest::invokeTcpSocketTunnelProxyApiCallPersist
|
||||
request.insert("method", method);
|
||||
request.insert("params", params);
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(request);
|
||||
|
||||
QByteArray payload = jsonDoc.toJson(QJsonDocument::Compact) + '\n';
|
||||
|
||||
QSignalSpy dataSpy(socket, &QSslSocket::readyRead);
|
||||
socket->write(jsonDoc.toJson(QJsonDocument::Compact) + '\n');
|
||||
|
||||
if (slipEnabled) {
|
||||
SlipDataProcessor::Frame frame;
|
||||
frame.socketAddress = 0x0000;
|
||||
frame.data = payload;
|
||||
socket->write(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
|
||||
} else {
|
||||
socket->write(payload);
|
||||
}
|
||||
|
||||
// FIXME: check why it waits the full time here
|
||||
dataSpy.wait(500);
|
||||
if (dataSpy.count() != 1) {
|
||||
if (dataSpy.count() < 1) {
|
||||
qWarning() << "No data received";
|
||||
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
|
||||
}
|
||||
|
||||
QByteArray data = socket->readAll();
|
||||
QByteArray data;
|
||||
if (slipEnabled) {
|
||||
QByteArray rawData = socket->readAll();
|
||||
QByteArray messageData;
|
||||
// Parse SLIP frame
|
||||
for (int i = 0; i < rawData.length(); i++) {
|
||||
quint8 byte = static_cast<quint8>(rawData.at(i));
|
||||
if (byte == SlipDataProcessor::ProtocolByteEnd) {
|
||||
// If there is no data...continue since it might be a starting END byte
|
||||
if (messageData.isEmpty())
|
||||
continue;
|
||||
|
||||
// Make sure the response ends with '}\n'
|
||||
if (!data.endsWith("}\n")) {
|
||||
qWarning() << "JSON data does not end with \"}\n\"";
|
||||
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
|
||||
break;
|
||||
messageData.clear();
|
||||
} else {
|
||||
messageData.append(rawData.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray deserializedData = SlipDataProcessor::deserializeData(messageData);
|
||||
SlipDataProcessor::Frame frame = SlipDataProcessor::parseFrame(deserializedData);
|
||||
if (frame.data.isEmpty()) {
|
||||
qWarning() << "Could not deserialize SLIP data";
|
||||
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
|
||||
}
|
||||
data = frame.data;
|
||||
} else {
|
||||
data = socket->readAll();
|
||||
// Make sure the response ends with '}\n'
|
||||
if (!data.endsWith("}\n")) {
|
||||
qWarning() << "JSON data does not end with \"}\n\"";
|
||||
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the response it a valid JSON string
|
||||
QJsonParseError error;
|
||||
jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "JSON parser error" << error.errorString();
|
||||
qWarning() << "JSON parser error" << error.errorString() << qUtf8Printable(data);
|
||||
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
|
||||
}
|
||||
|
||||
@ -718,7 +756,7 @@ QPair<QVariant, QSslSocket *> BaseTest::invokeTcpSocketTunnelProxyApiCallPersist
|
||||
return QPair<QVariant, QSslSocket *>(QVariant(), socket);
|
||||
}
|
||||
|
||||
QPair<QVariant, QWebSocket *> BaseTest::invokeWebSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params, QWebSocket *existingSocket)
|
||||
QPair<QVariant, QWebSocket *> BaseTest::invokeWebSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params, bool slipEnabled, QWebSocket *existingSocket)
|
||||
{
|
||||
QWebSocket *socket = nullptr;
|
||||
if (!existingSocket) {
|
||||
@ -740,16 +778,55 @@ QPair<QVariant, QWebSocket *> BaseTest::invokeWebSocketTunnelProxyApiCallPersist
|
||||
request.insert("method", method);
|
||||
request.insert("params", params);
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromVariant(request);
|
||||
QByteArray payload = jsonDoc.toJson(QJsonDocument::Compact) + '\n';
|
||||
|
||||
QSignalSpy dataSpy(socket, SIGNAL(textMessageReceived(QString)));
|
||||
socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact)));
|
||||
|
||||
if (slipEnabled) {
|
||||
SlipDataProcessor::Frame frame;
|
||||
frame.socketAddress = 0x0000;
|
||||
frame.data = payload;
|
||||
socket->sendTextMessage(QString(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame))));
|
||||
} else {
|
||||
socket->sendTextMessage(QString(payload));
|
||||
}
|
||||
|
||||
dataSpy.wait();
|
||||
|
||||
for (int i = 0; i < dataSpy.count(); i++) {
|
||||
// Make sure the response ends with '}\n'
|
||||
if (!dataSpy.at(i).last().toByteArray().endsWith("}\n")) {
|
||||
qWarning() << "JSON data does not end with \"}\n\"";
|
||||
return QPair<QVariant, QWebSocket *>(QVariant(), socket);
|
||||
QByteArray data;
|
||||
if (slipEnabled) {
|
||||
QByteArray rawData = dataSpy.at(i).last().toByteArray();
|
||||
QByteArray messageData;
|
||||
// Parse SLIP frame
|
||||
for (int i = 0; i < rawData.length(); i++) {
|
||||
quint8 byte = static_cast<quint8>(rawData.at(i));
|
||||
if (byte == SlipDataProcessor::ProtocolByteEnd) {
|
||||
// If there is no data...continue since it might be a starting END byte
|
||||
if (messageData.isEmpty())
|
||||
continue;
|
||||
|
||||
break;
|
||||
messageData.clear();
|
||||
} else {
|
||||
messageData.append(rawData.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray deserializedData = SlipDataProcessor::deserializeData(messageData);
|
||||
SlipDataProcessor::Frame frame = SlipDataProcessor::parseFrame(deserializedData);
|
||||
if (frame.data.isEmpty()) {
|
||||
qWarning() << "Could not deserialize SLIP data";
|
||||
return QPair<QVariant, QWebSocket *>(QVariant(), socket);
|
||||
}
|
||||
data = frame.data;
|
||||
} else {
|
||||
data = dataSpy.at(i).last().toByteArray();
|
||||
// Make sure the response ends with '}\n'
|
||||
if (!data.endsWith("}\n")) {
|
||||
qWarning() << "JSON data does not end with \"}\n\"";
|
||||
return QPair<QVariant, QWebSocket *>(QVariant(), socket);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the response it a valid JSON string
|
||||
@ -771,6 +848,7 @@ QPair<QVariant, QWebSocket *> BaseTest::invokeWebSocketTunnelProxyApiCallPersist
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << "No websocket data received...";
|
||||
m_commandCounter++;
|
||||
return QPair<QVariant, QWebSocket *>(QVariant(), socket);
|
||||
}
|
||||
|
||||
@ -97,8 +97,8 @@ protected:
|
||||
QVariant invokeTcpSocketTunnelProxyApiCall(const QString &method, const QVariantMap params = QVariantMap());
|
||||
QVariant injectTcpSocketTunnelProxyData(const QByteArray &data);
|
||||
|
||||
QPair<QVariant, QSslSocket *> invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), QSslSocket *existingSocket = nullptr);
|
||||
QPair<QVariant, QWebSocket *> invokeWebSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), QWebSocket *existingSocket = nullptr);
|
||||
QPair<QVariant, QSslSocket *> invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), bool slipEnabled = false, QSslSocket *existingSocket = nullptr);
|
||||
QPair<QVariant, QWebSocket *> invokeWebSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), bool slipEnabled = false, QWebSocket *existingSocket = nullptr);
|
||||
|
||||
|
||||
bool createRemoteConnection(const QString &token, const QString &nonce, QObject *parent);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
RESOURCES += ../resources/resources.qrc
|
||||
|
||||
include(../common/common.pri)
|
||||
|
||||
CONFIG += testcase
|
||||
QT += testlib
|
||||
|
||||
|
||||
Reference in New Issue
Block a user