Add first version of remote client connector in tests

more-debug
Simon Stürz 2018-08-02 21:20:22 +02:00
parent 8b14e3bc06
commit 43ebf38f30
8 changed files with 403 additions and 21 deletions

View File

@ -1,5 +1,4 @@
#include "engine.h"
#include "websocketserver.h"
#include "loggingcategories.h"
Engine *Engine::s_instance = nullptr;
@ -34,22 +33,20 @@ void Engine::start()
if (!m_running)
qCDebug(dcEngine()) << "Start server engine";
QUrl proxyUrl;
proxyUrl.setScheme("wss");
proxyUrl.setHost(m_webSocketServerHostAddress.toString());
proxyUrl.setPort(m_webSocketServerPort);
qCDebug(dcApplication()) << "Authentication server" << m_authenticationServerUrl.toString();
qCDebug(dcApplication()) << "Start server" << proxyUrl.toString();
qCDebug(dcEngine()) << "Starting websocket server";
// Init WebSocketServer
if (m_webSocketServer) {
delete m_webSocketServer;
m_webSocketServer = nullptr;
}
QUrl websocketServerUrl;
websocketServerUrl.setScheme("wss");
websocketServerUrl.setHost(m_webSocketServerHostAddress.toString());
websocketServerUrl.setPort(m_webSocketServerPort);
m_webSocketServer = new WebSocketServer(m_sslConfiguration, this);
m_webSocketServer->setServerUrl(proxyUrl);
m_webSocketServer->setServerUrl(websocketServerUrl);
m_webSocketServer->startServer();
setRunning(true);
@ -106,6 +103,11 @@ void Engine::setAuthenticationServerUrl(const QUrl &url)
m_authenticationServerUrl = url;
}
WebSocketServer *Engine::webSocketServer() const
{
return m_webSocketServer;
}
Engine::Engine(QObject *parent) :
QObject(parent)
{

View File

@ -6,7 +6,7 @@
#include <QHostAddress>
#include <QSslConfiguration>
class WebSocketServer;
#include "websocketserver.h"
class Engine : public QObject
{
@ -27,6 +27,8 @@ public:
void setSslConfiguration(const QSslConfiguration &configuration);
void setAuthenticationServerUrl(const QUrl &url);
WebSocketServer *webSocketServer() const;
private:
explicit Engine(QObject *parent = nullptr);
~Engine();

View File

@ -4,3 +4,254 @@ RemoteProxyConnector::RemoteProxyConnector(QObject *parent) : QObject(parent)
{
}
RemoteProxyConnector::~RemoteProxyConnector()
{
disconnectServer();
}
RemoteProxyConnector::Error RemoteProxyConnector::error() const
{
return m_error;
}
QString RemoteProxyConnector::errorString() const
{
QString errorString;
switch (m_error) {
case ErrorNoError:
errorString = "";
break;
case ErrorSocketError:
errorString = "Socket connection error occured: " + socketErrorString();
break;
case ErrorSslError:
errorString = "Socket SSL error occured.";
break;
case ErrorProxyNotResponding:
errorString = "The proxy server does not respond.";
break;
case ErrorProxyAuthenticationFailed:
errorString = "The authentication on the proxy server failed.";
break;
}
return errorString;
}
QAbstractSocket::SocketError RemoteProxyConnector::socketError() const
{
if (!m_webSocket)
return QAbstractSocket::UnknownSocketError;
return m_webSocket->error();
}
QString RemoteProxyConnector::socketErrorString() const
{
if (!m_webSocket)
return QString();
return m_webSocket->errorString();
}
QUrl RemoteProxyConnector::serverUrl() const
{
QUrl serverUrl;
serverUrl.setScheme("wss");
serverUrl.setHost(m_serverAddress.toString());
serverUrl.setPort(m_serverPort);
return serverUrl;
}
bool RemoteProxyConnector::isConnected() const
{
return m_state == StateConnected;
}
RemoteProxyConnector::ConnectionType RemoteProxyConnector::connectionType() const
{
return m_connectionType;
}
QHostAddress RemoteProxyConnector::serverAddress() const
{
return m_serverAddress;
}
quint16 RemoteProxyConnector::serverPort() const
{
return m_serverPort;
}
QList<QSslError> RemoteProxyConnector::ignoreSslErrors() const
{
return m_ignoreSslErrors;
}
void RemoteProxyConnector::setIgnoreSslErrors(const QList<QSslError> &errors)
{
m_ignoreSslErrors = errors;
}
bool RemoteProxyConnector::sendData(const QByteArray &data)
{
if (m_state != StateTunnelEstablished) {
qWarning() << "RemoteProxyClient: There is no established tunnel for" << serverUrl().toString() << "yet.";
return false;
}
if (!m_webSocket) {
qWarning() << "RemoteProxyClient: There is no websocket";
return false;
}
qint64 dataSendCount = m_webSocket->sendTextMessage(QString::fromUtf8(data));
if (dataSendCount != data.count()) {
qWarning() << "RemoteProxyClient: Could not send all data to" << serverUrl().toString();
return false;
}
return true;
}
void RemoteProxyConnector::setState(RemoteProxyConnector::State state)
{
if (m_state == state)
return;
qDebug() << "RemoteProxyClient: State changed" << state;
m_state = state;
emit stateChanged(m_state);
}
void RemoteProxyConnector::setError(RemoteProxyConnector::Error error)
{
if (m_error == error)
return;
qDebug() << "RemoteProxyClient: Error occured" << error;
m_error = error;
emit errorOccured(m_error);
}
void RemoteProxyConnector::setConnectionType(RemoteProxyConnector::ConnectionType type)
{
m_connectionType = type;
}
void RemoteProxyConnector::setServerAddress(const QHostAddress serverAddress)
{
m_serverAddress = serverAddress;
}
void RemoteProxyConnector::setServerPort(quint16 serverPort)
{
m_serverPort = serverPort;
}
void RemoteProxyConnector::onSocketConnected()
{
setState(StateConnected);
qDebug() << "RemoteProxyClient: Connected to" << serverUrl().toString();
// TODO: start authentication process
setState(StateAuthenticating);
}
void RemoteProxyConnector::onSocketDisconnected()
{
qDebug() << "RemoteProxyClient: Disconnected from" << serverUrl().toString();
setState(StateDisconnected);
}
void RemoteProxyConnector::onSocketError(QAbstractSocket::SocketError error)
{
qWarning() << "RemoteProxyClient: Socket error occured" << error;
setError(ErrorSocketError);
}
void RemoteProxyConnector::onSocketSslError(const QList<QSslError> &errors)
{
qWarning() << "RemoteProxyClient: Socket ssl errors occured" << errors;
foreach (const QSslError sslError, errors) {
qWarning() << "RemoteProxyClient: " << static_cast<int>(sslError.error()) << sslError.errorString();
}
qDebug() << m_ignoreSslErrors;
m_webSocket->ignoreSslErrors();
setError(ErrorSslError);
}
void RemoteProxyConnector::onSocketStateChanged(QAbstractSocket::SocketState state)
{
qDebug() << "RemoteProxyClient: Socket state changed" << state;
switch (state) {
case QAbstractSocket::ConnectingState:
case QAbstractSocket::HostLookupState:
setState(StateConnecting);
break;
case QAbstractSocket::ConnectedState:
setState(StateConnected);
break;
default:
setState(StateDisconnected);
break;
}
}
void RemoteProxyConnector::onTextMessageReceived(const QString &message)
{
// TODO: check if tunnel is established, if so, emit data received
qDebug() << "RemoteProxyClient: Data recived" << message;
}
void RemoteProxyConnector::onBinaryMessageReceived(const QByteArray &message)
{
Q_UNUSED(message);
}
bool RemoteProxyConnector::connectServer(RemoteProxyConnector::ConnectionType type, const QHostAddress &serverAddress, quint16 port)
{
setConnectionType(type);
setServerAddress(serverAddress);
setServerPort(port);
switch (m_connectionType) {
// TODO: currently only websocket support
case ConnectionTypeWebSocket:
if (m_webSocket) {
delete m_webSocket;
m_webSocket = nullptr;
}
setState(StateDisconnected);
m_webSocket = new QWebSocket("libnymea-remoteproxyclient", QWebSocketProtocol::VersionLatest, this);
m_webSocket->ignoreSslErrors(m_ignoreSslErrors);
connect(m_webSocket, &QWebSocket::connected, this, &RemoteProxyConnector::onSocketConnected);
connect(m_webSocket, &QWebSocket::disconnected, this, &RemoteProxyConnector::onSocketDisconnected);
connect(m_webSocket, &QWebSocket::textMessageReceived, this, &RemoteProxyConnector::onTextMessageReceived);
connect(m_webSocket, &QWebSocket::binaryMessageReceived, this, &RemoteProxyConnector::onBinaryMessageReceived);
connect(m_webSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
connect(m_webSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(onSocketSslError(QList<QSslError>)));
connect(m_webSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
setState(StateConnecting);
m_webSocket->open(serverUrl());
qDebug() << "RemoteProxyClient: Start connecting to" << serverUrl().toString();
return true;
}
return false;
}
void RemoteProxyConnector::disconnectServer()
{
}

View File

@ -2,16 +2,101 @@
#define REMOTEPROXYCONNECTOR_H
#include <QObject>
#include <QWebSocket>
#include <QHostAddress>
class RemoteProxyConnector : public QObject
{
Q_OBJECT
public:
enum ConnectionType {
ConnectionTypeWebSocket
};
Q_ENUM(ConnectionType)
enum State {
StateConnecting,
StateConnected,
StateAuthenticating,
StateWaitTunnel,
StateTunnelEstablished,
StateDisconnected
};
Q_ENUM(State)
enum Error {
ErrorNoError,
ErrorSocketError,
ErrorSslError,
ErrorProxyNotResponding,
ErrorProxyAuthenticationFailed
};
Q_ENUM(Error)
explicit RemoteProxyConnector(QObject *parent = nullptr);
~RemoteProxyConnector();
State state() const;
Error error() const;
QString errorString() const;
QAbstractSocket::SocketError socketError() const;
QString socketErrorString() const;
QUrl serverUrl() const;
bool isConnected() const;
bool tunnelEstablished() const;
ConnectionType connectionType() const;
QHostAddress serverAddress() const;
quint16 serverPort() const;
QList<QSslError> ignoreSslErrors() const;
void setIgnoreSslErrors(const QList<QSslError> &errors);
bool sendData(const QByteArray &data);
private:
ConnectionType m_connectionType = ConnectionTypeWebSocket;
QHostAddress m_serverAddress;
quint16 m_serverPort = 443;
State m_state = StateDisconnected;
Error m_error = ErrorNoError;
QList<QSslError> m_ignoreSslErrors;
bool m_tunnelEstablished = false;
QWebSocket *m_webSocket = nullptr;
void setState(State state);
void setError(Error error);
void setConnectionType(ConnectionType type);
void setServerAddress(const QHostAddress serverAddress);
void setServerPort(quint16 serverPort);
signals:
void connected();
void disconnected();
void tunnelEstablished();
void stateChanged(State state);
void errorOccured(Error error);
void dataReady(const QByteArray &data);
private slots:
void onSocketConnected();
void onSocketDisconnected();
void onSocketError(QAbstractSocket::SocketError error);
void onSocketSslError(const QList<QSslError> &errors);
void onSocketStateChanged(QAbstractSocket::SocketState state);
void onTextMessageReceived(const QString &message);
void onBinaryMessageReceived(const QByteArray &message);
public slots:
bool connectServer(ConnectionType type, const QHostAddress &serverAddress, quint16 port);
void disconnectServer();
};

View File

@ -90,6 +90,7 @@ int main(int argc, char *argv[])
s_loggingFilters.insert("Engine", true);
s_loggingFilters.insert("JsonRpc", true);
s_loggingFilters.insert("WebSocketServer", true);
s_loggingFilters.insert("WebSocketServerTraffic", false);
s_loggingFilters.insert("Authenticator", true);
s_loggingFilters.insert("ConnectionManager", true);
s_loggingFilters.insert("Debug", false);
@ -97,25 +98,31 @@ int main(int argc, char *argv[])
// command line parser
QCommandLineParser parser;
parser.addHelpOption();
parser.setApplicationDescription(QString("\nThe nymea remote proxy server. This server allowes nymea-cloud users and registered nymea deamons to establish a tunnel connection.\n\n"
parser.setApplicationDescription(QString("\nThe nymea remote proxy server. This server allowes nymea-cloud users and "
"registered nymea deamons to establish a tunnel connection.\n\n"
"Copyright %1 2018 Simon Stürz <simon.stuerz@guh.io>").arg(QChar(0xA9)));
QCommandLineOption logfileOption(QStringList() << "l" << "logging", "Write log file to the given logfile.", "logfile", "/var/log/nymea-remoteproxy.log");
QCommandLineOption logfileOption(QStringList() << "l" << "logging", "Write log file to the given logfile.",
"logfile", "/var/log/nymea-remoteproxy.log");
parser.addOption(logfileOption);
QCommandLineOption serverOption(QStringList() << "s" << "server", "The server address this proxy will listen on. Default is 127.0.0.1", "hostaddress", "127.0.0.1");
QCommandLineOption serverOption(QStringList() << "s" << "server", "The server address this proxy will listen on. "
"Default is 127.0.0.1", "hostaddress", "127.0.0.1");
parser.addOption(serverOption);
QCommandLineOption portOption(QStringList() << "p" << "port", "The proxy server port. Default is 1212", "port", "1212");
parser.addOption(portOption);
QCommandLineOption certOption(QStringList() << "c" <<"certificate", "The path to the SSL certificate used for this proxy server.", "certificate");
QCommandLineOption certOption(QStringList() << "c" <<"certificate", "The path to the SSL certificate used for "
"this proxy server.", "certificate");
parser.addOption(certOption);
QCommandLineOption certKeyOption(QStringList() << "k" << "certificate-key", "The path to the SSL certificate key used for this proxy server.", "certificate-key");
QCommandLineOption certKeyOption(QStringList() << "k" << "certificate-key", "The path to the SSL certificate key "
"used for this proxy server.", "certificate-key");
parser.addOption(certKeyOption);
QCommandLineOption authenticationUrlOption(QStringList() << "a" << "authentication-server", "The server url of the AWS authentication server.", "url", "https://127.0.0.1");
QCommandLineOption authenticationUrlOption(QStringList() << "a" << "authentication-server",
"The server url of the AWS authentication server.", "url", "https://127.0.0.1");
parser.addOption(authenticationUrlOption);
QCommandLineOption verboseOption(QStringList() << "v" << "verbose", "Print more verbose.");
@ -123,8 +130,10 @@ int main(int argc, char *argv[])
parser.process(application);
if (parser.isSet(verboseOption))
if (parser.isSet(verboseOption)) {
s_loggingFilters["Debug"] = true;
s_loggingFilters["WebSocketServerTraffic"] = true;
}
QLoggingCategory::installFilter(loggingCategoryFilter);

View File

@ -2,6 +2,9 @@
#include "engine.h"
#include "loggingcategories.h"
#include "remoteproxyconnector.h"
#include <QSignalSpy>
RemoteProxyTests::RemoteProxyTests(QObject *parent) :
QObject(parent)
@ -54,7 +57,6 @@ void RemoteProxyTests::startServer()
Engine::instance()->setWebSocketServerHostAddress(QHostAddress::LocalHost);
Engine::instance()->setSslConfiguration(m_sslConfiguration);
Engine::instance()->start();
}
void RemoteProxyTests::initTestCase()
@ -71,9 +73,19 @@ void RemoteProxyTests::cleanupTestCase()
void RemoteProxyTests::authenticate()
{
// Start the server
startServer();
// Connect to the server
RemoteProxyConnector *connector = new RemoteProxyConnector(this);
connector->setIgnoreSslErrors(QList<QSslError>() << QSslError::HostNameMismatch << QSslError::SelfSignedCertificate);
QSignalSpy spy(connector, &RemoteProxyConnector::error);
connector->connectServer(RemoteProxyConnector::ConnectionTypeWebSocket, QHostAddress::LocalHost, m_port);
//spy.wait();
connector->disconnectServer();
connector->deleteLater();
Engine::instance()->stop();
}
@ -94,4 +106,22 @@ void RemoteProxyTests::startStopServer()
QVERIFY(!Engine::exists());
}
void RemoteProxyTests::sslConfigurations()
{
// Start the server
startServer();
// Connect to the server
RemoteProxyConnector *connector = new RemoteProxyConnector(this);
connector->setIgnoreSslErrors(QList<QSslError>() << QSslError::HostNameMismatch << QSslError::SelfSignedCertificate);
QSignalSpy spy(connector, &RemoteProxyConnector::connected);
connector->connectServer(RemoteProxyConnector::ConnectionTypeWebSocket, QHostAddress::LocalHost, m_port);
spy.wait();
connector->disconnectServer();
connector->deleteLater();
Engine::instance()->stop();
}
QTEST_MAIN(RemoteProxyTests)

View File

@ -1,6 +1,7 @@
#ifndef NYMEA_REMOTEPROXY_TESTS_H
#define NYMEA_REMOTEPROXY_TESTS_H
#include <QUrl>
#include <QtTest>
#include <QSslKey>
#include <QObject>
@ -31,6 +32,7 @@ protected slots:
private slots:
void startStopServer();
void authenticate();
void sslConfigurations();
};

View File

@ -5,8 +5,9 @@ QT += testlib
TARGET = nymea-remoteproxy-tests
INCLUDEPATH += ../libnymea-remoteproxy
LIBS += -L$$top_builddir/libnymea-remoteproxy/ -lnymea-remoteproxy
INCLUDEPATH += ../libnymea-remoteproxy ../libnymea-remoteproxyclient
LIBS += -L$$top_builddir/libnymea-remoteproxy/ -lnymea-remoteproxy \
-L$$top_builddir/libnymea-remoteproxyclient/ -lnymea-remoteproxyclient \
RESOURCES += certificate.qrc