Add basic websocket server.
This commit is contained in:
parent
e892fe2b46
commit
8b14e3bc06
28
README.md
28
README.md
@ -22,8 +22,8 @@ In the build directory you can find the resulting library and binary files.
|
||||
If you want to start the proxy server from the build directory, you need to export the library path before starting the application:
|
||||
|
||||
|
||||
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/libnymea-remoteproxy
|
||||
$ ./server/nymea-remoteproxy
|
||||
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/libnymea-remoteproxy:$(pwd)/libnymea-remoteproxyclient
|
||||
$ ./server/nymea-remoteproxy -c ../nymea-remoteproxy/tests/test-certificate.crt -k ../nymea-remoteproxy/tests/test-certificate.key
|
||||
|
||||
|
||||
# Install
|
||||
@ -46,24 +46,28 @@ In order to get information about the server you can start the command with the
|
||||
|
||||
$ nymea-remoteproxy --help
|
||||
|
||||
usage: nymea-remoteproxy [options]
|
||||
Usage: nymea-remoteproxy [options]
|
||||
|
||||
The nymea remote proxy server.
|
||||
The nymea remote proxy server. This server allowes nymea-cloud users and registered nymea deamons to establish a tunnel connection.
|
||||
|
||||
Copyright © 2018 Simon Stürz <simon.stuerz@guh.io>
|
||||
|
||||
Options:
|
||||
-h, --help Displays this help.
|
||||
-l, --logging <logfile> Write log file to the given logfile.
|
||||
-s, --server <url> The authentication server url.
|
||||
-p, --port <port> The proxy server port.
|
||||
-c, --certificate <certificate> The path to the SSL certificate.
|
||||
-s, --server <hostaddress> The server address this proxy will
|
||||
listen on. Default is 127.0.0.1
|
||||
-p, --port <port> The proxy server port. Default is
|
||||
1212
|
||||
-c, --certificate <certificate> The path to the SSL certificate used
|
||||
for this proxy server.
|
||||
-k, --certificate-key <certificate-key> The path to the SSL certificate key
|
||||
(KEY).
|
||||
-v, --verbose Print the whole traffic.
|
||||
|
||||
|
||||
|
||||
used for this proxy server.
|
||||
-a, --authentication-server <url> The server url of the AWS
|
||||
authentication server.
|
||||
-v, --verbose Print more verbose.
|
||||
|
||||
|
||||
# Server API
|
||||
|
||||
Once a client connects to the proxy server, he must authenticate him self by passing the token received from the nymea-cloud mqtt connection request.
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "engine.h"
|
||||
#include "websocketserver.h"
|
||||
#include "loggingcategories.h"
|
||||
|
||||
Engine *Engine::s_instance = nullptr;
|
||||
@ -30,24 +31,40 @@ void Engine::destroy()
|
||||
|
||||
void Engine::start()
|
||||
{
|
||||
qCDebug(dcEngine()) << "Start server engine";
|
||||
if (!m_running)
|
||||
qCDebug(dcEngine()) << "Start server engine";
|
||||
|
||||
QUrl proxyUrl;
|
||||
proxyUrl.setScheme("wss");
|
||||
proxyUrl.setHost("0.0.0.0");
|
||||
proxyUrl.setPort(static_cast<int>(m_port));
|
||||
proxyUrl.setHost(m_webSocketServerHostAddress.toString());
|
||||
proxyUrl.setPort(m_webSocketServerPort);
|
||||
|
||||
qCDebug(dcApplication()) << "Authentication server" << m_authenticationServerUrl.toString();
|
||||
qCDebug(dcApplication()) << "Start server" << proxyUrl.toString();
|
||||
|
||||
// TODO: init stuff
|
||||
// Init WebSocketServer
|
||||
if (m_webSocketServer) {
|
||||
delete m_webSocketServer;
|
||||
m_webSocketServer = nullptr;
|
||||
}
|
||||
|
||||
m_webSocketServer = new WebSocketServer(m_sslConfiguration, this);
|
||||
m_webSocketServer->setServerUrl(proxyUrl);
|
||||
m_webSocketServer->startServer();
|
||||
|
||||
setRunning(true);
|
||||
}
|
||||
|
||||
void Engine::stop()
|
||||
{
|
||||
qCDebug(dcEngine()) << "Stop server engine";
|
||||
if (m_running)
|
||||
qCDebug(dcEngine()) << "Stop server engine";
|
||||
|
||||
// TODO: deinit stuff
|
||||
if (m_webSocketServer) {
|
||||
m_webSocketServer->stopServer();
|
||||
m_webSocketServer->deleteLater();
|
||||
m_webSocketServer = nullptr;
|
||||
}
|
||||
|
||||
setRunning(false);
|
||||
}
|
||||
@ -57,32 +74,49 @@ bool Engine::running() const
|
||||
return m_running;
|
||||
}
|
||||
|
||||
void Engine::setHost(const QHostAddress &hostAddress)
|
||||
void Engine::setWebSocketServerHostAddress(const QHostAddress &hostAddress)
|
||||
{
|
||||
m_hostAddress = hostAddress;
|
||||
qCDebug(dcEngine()) << "Websocket server host address:" << hostAddress;
|
||||
m_webSocketServerHostAddress = hostAddress;
|
||||
}
|
||||
|
||||
void Engine::setAuthenticationServerUrl(const QUrl &url)
|
||||
void Engine::setWebSocketServerPort(const quint16 &port)
|
||||
{
|
||||
m_authenticationServerUrl = url;
|
||||
}
|
||||
|
||||
void Engine::setPort(const quint16 &port)
|
||||
{
|
||||
m_port = port;
|
||||
qCDebug(dcEngine()) << "Websocket server port:" << port;
|
||||
m_webSocketServerPort = port;
|
||||
}
|
||||
|
||||
void Engine::setSslConfiguration(const QSslConfiguration &configuration)
|
||||
{
|
||||
qCDebug(dcEngine()) << "SSL Configuration:";
|
||||
qCDebug(dcEngine()) << " Common name:" << configuration.localCertificate().issuerInfo(QSslCertificate::CommonName);
|
||||
qCDebug(dcEngine()) << " Organisation:" << configuration.localCertificate().issuerInfo(QSslCertificate::Organization);
|
||||
qCDebug(dcEngine()) << " Organisation unit name:" << configuration.localCertificate().issuerInfo(QSslCertificate::OrganizationalUnitName);
|
||||
qCDebug(dcEngine()) << " Country name:" << configuration.localCertificate().issuerInfo(QSslCertificate::CountryName);
|
||||
qCDebug(dcEngine()) << " Locality name:" << configuration.localCertificate().issuerInfo(QSslCertificate::LocalityName);
|
||||
qCDebug(dcEngine()) << " State/Province:" << configuration.localCertificate().issuerInfo(QSslCertificate::StateOrProvinceName);
|
||||
qCDebug(dcEngine()) << " Email address:" << configuration.localCertificate().issuerInfo(QSslCertificate::EmailAddress);
|
||||
|
||||
m_sslConfiguration = configuration;
|
||||
}
|
||||
|
||||
void Engine::setAuthenticationServerUrl(const QUrl &url)
|
||||
{
|
||||
qCDebug(dcEngine()) << "Authentication server URL" << url.toString();
|
||||
m_authenticationServerUrl = url;
|
||||
}
|
||||
|
||||
Engine::Engine(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Engine::~Engine()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void Engine::setRunning(bool running)
|
||||
{
|
||||
if (m_running == running)
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
#include <QHostAddress>
|
||||
#include <QSslConfiguration>
|
||||
|
||||
class WebSocketServer;
|
||||
|
||||
class Engine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -19,23 +21,27 @@ public:
|
||||
|
||||
bool running() const;
|
||||
|
||||
void setHost(const QHostAddress &hostAddress);
|
||||
void setAuthenticationServerUrl(const QUrl &url);
|
||||
void setPort(const quint16 &port);
|
||||
void setWebSocketServerHostAddress(const QHostAddress &hostAddress);
|
||||
void setWebSocketServerPort(const quint16 &port);
|
||||
|
||||
void setSslConfiguration(const QSslConfiguration &configuration);
|
||||
void setAuthenticationServerUrl(const QUrl &url);
|
||||
|
||||
private:
|
||||
explicit Engine(QObject *parent = nullptr);
|
||||
~Engine();
|
||||
static Engine *s_instance;
|
||||
|
||||
bool m_running = false;
|
||||
|
||||
quint16 m_port = 0;
|
||||
QHostAddress m_hostAddress;
|
||||
QSslConfiguration m_sslConfiguration;
|
||||
quint16 m_webSocketServerPort = 1212;
|
||||
QHostAddress m_webSocketServerHostAddress = QHostAddress::LocalHost;
|
||||
|
||||
QSslConfiguration m_sslConfiguration;
|
||||
QUrl m_authenticationServerUrl;
|
||||
|
||||
WebSocketServer *m_webSocketServer = nullptr;
|
||||
|
||||
void setRunning(bool running);
|
||||
|
||||
signals:
|
||||
|
||||
@ -5,8 +5,21 @@ TARGET = nymea-remoteproxy
|
||||
|
||||
HEADERS += \
|
||||
engine.h \
|
||||
loggingcategories.h
|
||||
loggingcategories.h \
|
||||
transportinterface.h \
|
||||
websocketserver.h
|
||||
|
||||
SOURCES += \
|
||||
engine.cpp \
|
||||
loggingcategories.cpp
|
||||
loggingcategories.cpp \
|
||||
transportinterface.cpp \
|
||||
websocketserver.cpp
|
||||
|
||||
|
||||
# install header file with relative subdirectory
|
||||
for(header, HEADERS) {
|
||||
path = /usr/include/nymea-remoteproxy/$${dirname(header)}
|
||||
eval(headers_$${path}.files += $${header})
|
||||
eval(headers_$${path}.path = $${path})
|
||||
eval(INSTALLS *= headers_$${path})
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ Q_LOGGING_CATEGORY(dcApplication, "Application")
|
||||
Q_LOGGING_CATEGORY(dcEngine, "Engine")
|
||||
Q_LOGGING_CATEGORY(dcJsonRpc, "JsonRpc")
|
||||
Q_LOGGING_CATEGORY(dcWebSocketServer, "WebSocketServer")
|
||||
Q_LOGGING_CATEGORY(dcWebSocketServerTraffic, "WebSocketServerTraffic")
|
||||
Q_LOGGING_CATEGORY(dcAuthenticator, "Authenticator")
|
||||
Q_LOGGING_CATEGORY(dcDebug, "Debug")
|
||||
Q_LOGGING_CATEGORY(dcConnectionManager, "ConnectionManager")
|
||||
|
||||
@ -8,6 +8,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcApplication)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcEngine)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcJsonRpc)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServer)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServerTraffic)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcAuthenticator)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcConnectionManager)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcDebug)
|
||||
|
||||
12
libnymea-remoteproxy/transportinterface.cpp
Normal file
12
libnymea-remoteproxy/transportinterface.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "transportinterface.h"
|
||||
|
||||
TransportInterface::TransportInterface(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TransportInterface::~TransportInterface()
|
||||
{
|
||||
|
||||
}
|
||||
27
libnymea-remoteproxy/transportinterface.h
Normal file
27
libnymea-remoteproxy/transportinterface.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef TRANSPORTINTERFACE_H
|
||||
#define TRANSPORTINTERFACE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TransportInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TransportInterface(QObject *parent = nullptr);
|
||||
virtual ~TransportInterface() = 0;
|
||||
|
||||
virtual void sendData(const QUuid &clientId, const QByteArray &data) = 0;
|
||||
virtual void sendData(const QList<QUuid> &clients, const QByteArray &data) = 0;
|
||||
|
||||
signals:
|
||||
void clientConnected(const QUuid &clientId);
|
||||
void clientDisconnected(const QUuid &clientId);
|
||||
void dataAvailable(const QUuid &clientId, const QByteArray &data);
|
||||
|
||||
public slots:
|
||||
virtual bool startServer() = 0;
|
||||
virtual bool stopServer() = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // TRANSPORTINTERFACE_H
|
||||
162
libnymea-remoteproxy/websocketserver.cpp
Normal file
162
libnymea-remoteproxy/websocketserver.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
#include "websocketserver.h"
|
||||
#include "loggingcategories.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
WebSocketServer::WebSocketServer(const QSslConfiguration &sslConfiguration, QObject *parent) :
|
||||
TransportInterface(parent),
|
||||
m_sslConfiguration(sslConfiguration)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
WebSocketServer::~WebSocketServer()
|
||||
{
|
||||
stopServer();
|
||||
}
|
||||
|
||||
QUrl WebSocketServer::serverUrl() const
|
||||
{
|
||||
return m_serverUrl;
|
||||
}
|
||||
|
||||
void WebSocketServer::setServerUrl(const QUrl &serverUrl)
|
||||
{
|
||||
m_serverUrl = serverUrl;
|
||||
}
|
||||
|
||||
bool WebSocketServer::running() const
|
||||
{
|
||||
return m_server->isListening();
|
||||
}
|
||||
|
||||
QSslConfiguration WebSocketServer::sslConfiguration() const
|
||||
{
|
||||
return m_sslConfiguration;
|
||||
}
|
||||
|
||||
void WebSocketServer::sendData(const QUuid &clientId, const QByteArray &data)
|
||||
{
|
||||
QWebSocket *client = nullptr;
|
||||
client = m_clientList.value(clientId);
|
||||
if (client) {
|
||||
qCDebug(dcWebSocketServerTraffic()) << "--> Sending data to client:" << data;
|
||||
client->sendTextMessage(data + '\n');
|
||||
} else {
|
||||
qCWarning(dcWebSocketServer()) << "Client" << clientId << "unknown to this transport";
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketServer::sendData(const QList<QUuid> &clients, const QByteArray &data)
|
||||
{
|
||||
foreach (const QUuid &client, clients) {
|
||||
sendData(client, data);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketServer::onClientConnected()
|
||||
{
|
||||
// Got a new client connected
|
||||
QWebSocket *client = m_server->nextPendingConnection();
|
||||
|
||||
// Check websocket version
|
||||
if (client->version() != QWebSocketProtocol::Version13) {
|
||||
qCWarning(dcWebSocketServer()) << "Client with invalid protocol version" << client->version() << ". Rejecting.";
|
||||
client->close(QWebSocketProtocol::CloseCodeProtocolError, QString("invalid protocol version: %1 != Supported Version 13").arg(client->version()));
|
||||
delete client;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new uuid for this connection
|
||||
QUuid clientId = QUuid::createUuid();
|
||||
qCDebug(dcWebSocketServer()) << "New client connected:" << client->peerAddress().toString() << clientId;
|
||||
|
||||
// Append the new client to the client list
|
||||
m_clientList.insert(clientId, client);
|
||||
|
||||
connect(client, SIGNAL(pong(quint64,QByteArray)), this, SLOT(onPing(quint64,QByteArray)));
|
||||
connect(client, SIGNAL(binaryMessageReceived(QByteArray)), this, SLOT(onBinaryMessageReceived(QByteArray)));
|
||||
connect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(onTextMessageReceived(QString)));
|
||||
connect(client, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onClientError(QAbstractSocket::SocketError)));
|
||||
connect(client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected()));
|
||||
|
||||
emit clientConnected(clientId);
|
||||
}
|
||||
|
||||
void WebSocketServer::onClientDisconnected()
|
||||
{
|
||||
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
||||
QUuid clientId = m_clientList.key(client);
|
||||
|
||||
qCDebug(dcWebSocketServer()) << "Client disconnected:" << client->peerAddress().toString() << clientId;
|
||||
|
||||
m_clientList.take(clientId)->deleteLater();
|
||||
emit clientDisconnected(clientId);
|
||||
}
|
||||
|
||||
void WebSocketServer::onBinaryMessageReceived(const QByteArray &data)
|
||||
{
|
||||
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
||||
qCDebug(dcWebSocketServerTraffic()) << "<-- Binary message from" << client->peerAddress().toString() << ":" << data;
|
||||
}
|
||||
|
||||
void WebSocketServer::onTextMessageReceived(const QString &message)
|
||||
{
|
||||
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
||||
qCDebug(dcWebSocketServerTraffic()) << "Text message from" << client->peerAddress().toString() << ":" << message;
|
||||
emit dataAvailable(m_clientList.key(client), message.toUtf8());
|
||||
}
|
||||
|
||||
void WebSocketServer::onClientError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
||||
qCWarning(dcWebSocketServer()) << "Client error occured:" << error << client->errorString();
|
||||
}
|
||||
|
||||
void WebSocketServer::onServerError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
qCWarning(dcWebSocketServer()) << "Server error occured:" << error << m_server->errorString();
|
||||
}
|
||||
|
||||
void WebSocketServer::onPing(quint64 elapsedTime, const QByteArray &payload)
|
||||
{
|
||||
QWebSocket *client = static_cast<QWebSocket *>(sender());
|
||||
qCDebug(dcWebSocketServer) << "ping response" << client->peerAddress() << elapsedTime << payload;
|
||||
}
|
||||
|
||||
bool WebSocketServer::startServer()
|
||||
{
|
||||
m_server = new QWebSocketServer(QCoreApplication::applicationName(), QWebSocketServer::SecureMode, this);
|
||||
m_server->setSslConfiguration(m_sslConfiguration);
|
||||
|
||||
connect (m_server, &QWebSocketServer::newConnection, this, &WebSocketServer::onClientConnected);
|
||||
connect (m_server, &QWebSocketServer::acceptError, this, &WebSocketServer::onServerError);
|
||||
|
||||
qCDebug(dcWebSocketServer()) << "Starting server" << m_server->serverName() << m_serverUrl.toString();
|
||||
if (!m_server->listen(QHostAddress(m_serverUrl.host()), static_cast<quint16>(m_serverUrl.port()))) {
|
||||
qCWarning(dcWebSocketServer()) << "Server" << m_server->serverName() << "could not listen on" << serverUrl().toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
qCDebug(dcWebSocketServer()) << "Server started successfully.";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebSocketServer::stopServer()
|
||||
{
|
||||
|
||||
// Clean up client connections
|
||||
foreach (QWebSocket *client, m_clientList.values()) {
|
||||
client->close(QWebSocketProtocol::CloseCodeNormal, "Stop server");
|
||||
}
|
||||
|
||||
// Delete the server object
|
||||
if (m_server) {
|
||||
qCDebug(dcWebSocketServer()) << "Stop server" << m_server->serverName() << m_serverUrl.toString();
|
||||
m_server->close();
|
||||
delete m_server;
|
||||
m_server = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
53
libnymea-remoteproxy/websocketserver.h
Normal file
53
libnymea-remoteproxy/websocketserver.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef WEBSOCKETSERVER_H
|
||||
#define WEBSOCKETSERVER_H
|
||||
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <QObject>
|
||||
#include <QWebSocket>
|
||||
#include <QWebSocketServer>
|
||||
#include <QSslConfiguration>
|
||||
|
||||
#include "transportinterface.h"
|
||||
|
||||
class WebSocketServer : public TransportInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WebSocketServer(const QSslConfiguration &sslConfiguration, QObject *parent = nullptr);
|
||||
~WebSocketServer() override;
|
||||
|
||||
QUrl serverUrl() const;
|
||||
void setServerUrl(const QUrl &serverUrl);
|
||||
|
||||
bool running() const;
|
||||
|
||||
QSslConfiguration sslConfiguration() const;
|
||||
|
||||
void sendData(const QUuid &clientId, const QByteArray &data) override;
|
||||
void sendData(const QList<QUuid> &clients, const QByteArray &data) override;
|
||||
|
||||
private:
|
||||
QUrl m_serverUrl;
|
||||
QWebSocketServer *m_server = nullptr;
|
||||
QSslConfiguration m_sslConfiguration;
|
||||
bool m_enabled = false;
|
||||
|
||||
QHash<QUuid, QWebSocket *> m_clientList;
|
||||
|
||||
private slots:
|
||||
void onClientConnected();
|
||||
void onClientDisconnected();
|
||||
void onBinaryMessageReceived(const QByteArray &data);
|
||||
void onTextMessageReceived(const QString &message);
|
||||
void onClientError(QAbstractSocket::SocketError error);
|
||||
void onServerError(QAbstractSocket::SocketError error);
|
||||
void onPing(quint64 elapsedTime, const QByteArray & payload);
|
||||
|
||||
public slots:
|
||||
bool startServer() override;
|
||||
bool stopServer() override;
|
||||
|
||||
};
|
||||
|
||||
#endif // WEBSOCKETSERVER_H
|
||||
20
libnymea-remoteproxyclient/libnymea-remoteproxyclient.pro
Normal file
20
libnymea-remoteproxyclient/libnymea-remoteproxyclient.pro
Normal file
@ -0,0 +1,20 @@
|
||||
include(../nymea-remoteproxy.pri)
|
||||
|
||||
TEMPLATE = lib
|
||||
TARGET = nymea-remoteproxyclient
|
||||
|
||||
HEADERS += \
|
||||
remoteproxyconnector.h
|
||||
|
||||
|
||||
SOURCES += \
|
||||
remoteproxyconnector.cpp
|
||||
|
||||
|
||||
# install header file with relative subdirectory
|
||||
for(header, HEADERS) {
|
||||
path = /usr/include/nymea-remoteproxyclient/$${dirname(header)}
|
||||
eval(headers_$${path}.files += $${header})
|
||||
eval(headers_$${path}.path = $${path})
|
||||
eval(INSTALLS *= headers_$${path})
|
||||
}
|
||||
6
libnymea-remoteproxyclient/remoteproxyconnector.cpp
Normal file
6
libnymea-remoteproxyclient/remoteproxyconnector.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "remoteproxyconnector.h"
|
||||
|
||||
RemoteProxyConnector::RemoteProxyConnector(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
18
libnymea-remoteproxyclient/remoteproxyconnector.h
Normal file
18
libnymea-remoteproxyclient/remoteproxyconnector.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef REMOTEPROXYCONNECTOR_H
|
||||
#define REMOTEPROXYCONNECTOR_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class RemoteProxyConnector : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RemoteProxyConnector(QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
};
|
||||
|
||||
#endif // REMOTEPROXYCONNECTOR_H
|
||||
@ -1,4 +1,4 @@
|
||||
QT *= network
|
||||
QT *= network websockets
|
||||
QT -= gui
|
||||
|
||||
CONFIG += c++11 console
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
include(nymea-remoteproxy.pri)
|
||||
|
||||
TEMPLATE=subdirs
|
||||
SUBDIRS += server libnymea-remoteproxy tests
|
||||
SUBDIRS += server libnymea-remoteproxy libnymea-remoteproxyclient tests
|
||||
|
||||
server.depends = libnymea-remoteproxy
|
||||
tests.depends = libnymea-remoteproxy
|
||||
tests.depends = libnymea-remoteproxy libnymea-remoteproxyclient
|
||||
|
||||
|
||||
@ -97,25 +97,28 @@ int main(int argc, char *argv[])
|
||||
// command line parser
|
||||
QCommandLineParser parser;
|
||||
parser.addHelpOption();
|
||||
parser.setApplicationDescription(QString("\nThe nymea remote proxy server.\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");
|
||||
parser.addOption(logfileOption);
|
||||
|
||||
QCommandLineOption serverOption(QStringList() << "s" << "server", "The authentication server url.", "url", "http://localhost:8000");
|
||||
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.", "port", "1212");
|
||||
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.", "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 (KEY).", "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 verboseOption(QStringList() << "v" << "verbose", "Print the whole traffic.");
|
||||
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.");
|
||||
parser.addOption(verboseOption);
|
||||
|
||||
parser.process(application);
|
||||
@ -141,37 +144,24 @@ int main(int argc, char *argv[])
|
||||
s_loggingEnabled = true;
|
||||
}
|
||||
|
||||
qCDebug(dcApplication()) << "==============================================";
|
||||
qCDebug(dcApplication()) << "Starting" << application.applicationName() << application.applicationVersion();
|
||||
qCDebug(dcApplication()) << "==============================================";
|
||||
|
||||
if (s_loggingEnabled)
|
||||
qCDebug(dcApplication()) << "Logging enabled. Writing logs to" << s_logFile.fileName();
|
||||
|
||||
// Authentication server url
|
||||
QUrl serverUrl("http://localhost:8000");
|
||||
if (parser.isSet(serverOption))
|
||||
serverUrl = QUrl(parser.value(serverOption));
|
||||
|
||||
if (!serverUrl.isValid()) {
|
||||
qCCritical(dcApplication()) << "Invalid authentication server url:" << parser.value(serverOption);
|
||||
// Proxy server host address
|
||||
QHostAddress serverHostAddress = QHostAddress(parser.value(serverOption));
|
||||
if (serverHostAddress.isNull()) {
|
||||
qCCritical(dcApplication()) << "Invalid hostaddress for the proxy server:" << parser.value(serverOption);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Port
|
||||
uint port = 1212;
|
||||
if (parser.isSet(portOption)) {
|
||||
bool ok = false;
|
||||
port = parser.value(portOption).toUInt(&ok);
|
||||
if (!ok) {
|
||||
qCCritical(dcApplication()) << "Invalud port value:" << parser.value(portOption);
|
||||
exit(-1);
|
||||
}
|
||||
bool ok = false;
|
||||
uint port = parser.value(portOption).toUInt(&ok);
|
||||
if (!ok) {
|
||||
qCCritical(dcApplication()) << "Invalid port value:" << parser.value(portOption);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (port > 65535) {
|
||||
qCCritical(dcApplication()) << "Port value is out of range:" << parser.value(portOption);
|
||||
exit(-1);
|
||||
}
|
||||
if (port > 65535) {
|
||||
qCCritical(dcApplication()) << "Port value is out of range:" << parser.value(portOption);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// SSL certificate
|
||||
@ -198,18 +188,40 @@ int main(int argc, char *argv[])
|
||||
if (parser.isSet(certKeyOption)) {
|
||||
QFile certKeyFile(parser.value(certKeyOption));
|
||||
if (!certKeyFile.open(QIODevice::ReadOnly)) {
|
||||
qCCritical(dcApplication()) << "ERROR: Could not open certificate key file:" << parser.value(certKeyOption) << certKeyFile.errorString();
|
||||
qCCritical(dcApplication()) << "Could not open certificate key file:" << parser.value(certKeyOption) << certKeyFile.errorString();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
QSslKey sslKey(&certKeyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
||||
qCDebug(dcApplication()) << "Loaded successfully certificate key" << parser.value(certKeyOption);
|
||||
certKeyFile.close();
|
||||
sslConfiguration.setPrivateKey(sslKey);
|
||||
}
|
||||
|
||||
Engine::instance()->setAuthenticationServerUrl(serverUrl);
|
||||
Engine::instance()->setPort(static_cast<quint16>(port));
|
||||
if (sslConfiguration.isNull()) {
|
||||
qCCritical(dcApplication()) << "No SSL configuration specified. The server does not suppoert insecure connections.";
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Authentication server url
|
||||
QUrl authenticationServerUrl(parser.value(authenticationUrlOption));
|
||||
if (!authenticationServerUrl.isValid()) {
|
||||
qCCritical(dcApplication()) << "Invalid authentication server url:" << parser.value(authenticationUrlOption);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
qCDebug(dcApplication()) << "==============================================";
|
||||
qCDebug(dcApplication()) << "Starting" << application.applicationName() << application.applicationVersion();
|
||||
qCDebug(dcApplication()) << "==============================================";
|
||||
|
||||
if (s_loggingEnabled)
|
||||
qCDebug(dcApplication()) << "Logging enabled. Writing logs to" << s_logFile.fileName();
|
||||
|
||||
// Configure and start the engines
|
||||
Engine::instance()->setWebSocketServerHostAddress(serverHostAddress);
|
||||
Engine::instance()->setWebSocketServerPort(static_cast<quint16>(port));
|
||||
Engine::instance()->setSslConfiguration(sslConfiguration);
|
||||
Engine::instance()->setAuthenticationServerUrl(authenticationServerUrl);
|
||||
Engine::instance()->start();
|
||||
|
||||
return application.exec();
|
||||
|
||||
6
tests/certificate.qrc
Normal file
6
tests/certificate.qrc
Normal file
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>test-certificate.crt</file>
|
||||
<file>test-certificate.key</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@ -6,7 +6,28 @@
|
||||
RemoteProxyTests::RemoteProxyTests(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
QFile certificateFile(":/test-certificate.crt");
|
||||
if (!certificateFile.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open resource file" << certificateFile.fileName();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
QByteArray certificateData = certificateFile.readAll();
|
||||
//qDebug() << "Certificate:" << endl << qUtf8Printable(certificateData);
|
||||
|
||||
QFile keyFile(":/test-certificate.key");
|
||||
if (!keyFile.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open resource file" << keyFile.fileName();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
QByteArray keyData = keyFile.readAll();
|
||||
//qDebug() << "Certificate key:" << endl << qUtf8Printable(keyData);
|
||||
|
||||
m_sslConfiguration.setPrivateKey(QSslKey(keyData, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey));
|
||||
m_sslConfiguration.setLocalCertificate(QSslCertificate(certificateData, QSsl::Pem));
|
||||
m_sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||
m_sslConfiguration.setProtocol(QSsl::TlsV1_2OrLater);
|
||||
}
|
||||
|
||||
void RemoteProxyTests::cleanUpEngine()
|
||||
@ -25,6 +46,17 @@ void RemoteProxyTests::restartEngine()
|
||||
QVERIFY(Engine::exists());
|
||||
}
|
||||
|
||||
void RemoteProxyTests::startServer()
|
||||
{
|
||||
restartEngine();
|
||||
|
||||
Engine::instance()->setWebSocketServerPort(m_port);
|
||||
Engine::instance()->setWebSocketServerHostAddress(QHostAddress::LocalHost);
|
||||
Engine::instance()->setSslConfiguration(m_sslConfiguration);
|
||||
Engine::instance()->start();
|
||||
|
||||
}
|
||||
|
||||
void RemoteProxyTests::initTestCase()
|
||||
{
|
||||
qCDebug(dcApplication()) << "Init test case.";
|
||||
@ -37,6 +69,14 @@ void RemoteProxyTests::cleanupTestCase()
|
||||
cleanUpEngine();
|
||||
}
|
||||
|
||||
void RemoteProxyTests::authenticate()
|
||||
{
|
||||
startServer();
|
||||
|
||||
|
||||
Engine::instance()->stop();
|
||||
}
|
||||
|
||||
void RemoteProxyTests::startStopServer()
|
||||
{
|
||||
cleanUpEngine();
|
||||
|
||||
@ -2,7 +2,11 @@
|
||||
#define NYMEA_REMOTEPROXY_TESTS_H
|
||||
|
||||
#include <QtTest>
|
||||
#include <QSslKey>
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
#include <QSslCertificate>
|
||||
#include <QSslConfiguration>
|
||||
|
||||
class RemoteProxyTests : public QObject
|
||||
{
|
||||
@ -11,8 +15,13 @@ public:
|
||||
explicit RemoteProxyTests(QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
quint16 m_port = 1212;
|
||||
QHostAddress m_serverAddress = QHostAddress::LocalHost;
|
||||
QSslConfiguration m_sslConfiguration;
|
||||
|
||||
void cleanUpEngine();
|
||||
void restartEngine();
|
||||
void startServer();
|
||||
|
||||
protected slots:
|
||||
void initTestCase();
|
||||
@ -21,9 +30,7 @@ protected slots:
|
||||
|
||||
private slots:
|
||||
void startStopServer();
|
||||
|
||||
|
||||
|
||||
void authenticate();
|
||||
|
||||
};
|
||||
|
||||
|
||||
23
tests/test-certificate.crt
Normal file
23
tests/test-certificate.crt
Normal file
@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID4TCCAsmgAwIBAgIJAPikDhNDwsqGMA0GCSqGSIb3DQEBCwUAMIGFMQswCQYD
|
||||
VQQGEwJBVDEPMA0GA1UECAwGVmllbm5hMQ8wDQYDVQQHDAZWaWVubmExEjAQBgNV
|
||||
BAoMCWd1aCBHbWJILjELMAkGA1UECwwCUUExEjAQBgNVBAMMCWxvY2FsaG9zdDEf
|
||||
MB0GCSqGSIb3DQEJARYQZGV2ZWxvcGVyQGd1aC5pbzAgFw0xODA4MDIxNDU3NTZa
|
||||
GA8yMTE4MDcwOTE0NTc1NlowgYUxCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZWaWVu
|
||||
bmExDzANBgNVBAcMBlZpZW5uYTESMBAGA1UECgwJZ3VoIEdtYkguMQswCQYDVQQL
|
||||
DAJRQTESMBAGA1UEAwwJbG9jYWxob3N0MR8wHQYJKoZIhvcNAQkBFhBkZXZlbG9w
|
||||
ZXJAZ3VoLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoNm4oJfX
|
||||
4E7RNzJsscs1+sAgDG2I0fDAwHM1evStk9hjZTHy+T2aLPih1nkAiZvt5mso8v6W
|
||||
rrRc1a3MtHinJRXP4wv/WPLxACmEzNhEzRpl4d+JmcOHwNclT3xjRJCBsW5SNxr1
|
||||
Z2GQICTFJeIBmyHzVcLNGnlrXL61tkSeDCaY3TiaxCRXgtPBtdoJttFFJTXWld2P
|
||||
V4QSlkPA+9edQmdG6jr5MW/LxxdEyp1kqp+9erZ0BbLFly/sNv2hL1Gy6rlPBdxJ
|
||||
S09IYXAvqCjSwzA1VN0P35/iHZLigrwe9bt5jMC8bYAU1yOKrMHVwQhSBe6fij7g
|
||||
EezK1C90BWLOgwIDAQABo1AwTjAdBgNVHQ4EFgQUrT5VDdjUEOzKPWRYelLAI3Nw
|
||||
4uUwHwYDVR0jBBgwFoAUrT5VDdjUEOzKPWRYelLAI3Nw4uUwDAYDVR0TBAUwAwEB
|
||||
/zANBgkqhkiG9w0BAQsFAAOCAQEARHMVjrBEKbfNXTsfNAQcSRkAMkCbWG5/NQDl
|
||||
wHbNEu+VV0OF0AJ0IE1NCC4ZLuyUxSztMJdTOSM2lLmSGjCWyUlKhGvq79MDlomN
|
||||
jHYLPhSV3pYwaO86qNTtBqAQ1YXuauFboxHrpvsXx5a6ivPl1UyiDP9EtHDvkUpo
|
||||
tSW0HBC0n+o78vHbGXyzvlyI25UE9jnIDPD2K3jm7bcGqiqrDgT06QyE7GD43eeL
|
||||
0X6dBKYQWUWcG6cBR9Cvoj1Ec3IcXuprHwfbNrVtYOwdO7678U6gJuk1GeblcsgX
|
||||
9HfkjP61NIqUpu2U0BQrd3mDkjsNuta2IKLW7ojALRpP1shorA==
|
||||
-----END CERTIFICATE-----
|
||||
28
tests/test-certificate.key
Normal file
28
tests/test-certificate.key
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCg2bigl9fgTtE3
|
||||
MmyxyzX6wCAMbYjR8MDAczV69K2T2GNlMfL5PZos+KHWeQCJm+3mayjy/pautFzV
|
||||
rcy0eKclFc/jC/9Y8vEAKYTM2ETNGmXh34mZw4fA1yVPfGNEkIGxblI3GvVnYZAg
|
||||
JMUl4gGbIfNVws0aeWtcvrW2RJ4MJpjdOJrEJFeC08G12gm20UUlNdaV3Y9XhBKW
|
||||
Q8D7151CZ0bqOvkxb8vHF0TKnWSqn716tnQFssWXL+w2/aEvUbLquU8F3ElLT0hh
|
||||
cC+oKNLDMDVU3Q/fn+IdkuKCvB71u3mMwLxtgBTXI4qswdXBCFIF7p+KPuAR7MrU
|
||||
L3QFYs6DAgMBAAECggEAIp+bAV6OBmeQ21vMT90ZKneOrn4pdF9pbkOeYHAoqOls
|
||||
Q80jqwjalhGS7JKxTe21oOKyNtQcZVDHpus5ZCOQfwgZ7pqXhXMN82X8gweOj8O9
|
||||
6Ifwm5ueLiFhk6GH0Kt2b1X69oekCXPTzUd89HGYvObq9SvwcGFwfc9DWA+fIagZ
|
||||
3PRZunEIJlWYRtksMCdsiHMOCeUt2alz4XMYLb0ng+dHEmjnQEWPGPbKMBUnIM0t
|
||||
g5yKZCJ0i/FG+to27+hio9dzG4K/bRLvwlFswLZRfEcvqkBXpQ+aATUNA7jQpRxp
|
||||
e6sWAhpiS+tn2nMniLI+voe0W+v0s+5XLtDRX7LFUQKBgQDQhBTcKXXdnykIOslm
|
||||
p9D+rFaUf10aXpFz3aq0jjy5lHkCXOPc1NTG29e7Ysi8xKwlZdSca6l+zu0zQf1m
|
||||
cW4r/ejzwKM3qrLLeh4Hn7wpoWpliH5Z7BCdkTmeqGJI/9h1VxXgjc6j1DTGsyGb
|
||||
nezNEsNEYmaSZZreConLVGHcuwKBgQDFet79wnbTnqPohySLJy4Q3j+zYXHs/+3I
|
||||
dgRSg9Q/W2uE2ki58oDc10CRgGCet3iyXL5Pbq+QO/N50jpJHKow0DZqka3PTaLz
|
||||
hqqj1FK08/Ual6cZ4TViZ09DE6TnQYXEKtdqakr+iNhNu1O4NQpvW+fjMUaSTqTy
|
||||
DCfqMVrc2QKBgDc6JIRDfvJaC3Ygi+Nio1owryXZrgEj9wZnOaVk3vSQPJTBoaCF
|
||||
xm19IOCCw4Qr2yR0miFr6pKpn+2plsIhXPrN33tgFFrUHVMDXxzJ0/56wEFwK3cJ
|
||||
rUTuiAJbutx21xANJsA5DvgZZnkSTN54r5WJWKquHVi9DI2u4Nw310utAoGBALRm
|
||||
m6DbbivB52A7XLHWF+oG7dBTu3xTDAitbyNmFJXQUPdrZTahBwE3jl+dFCbJ2nOt
|
||||
75imt7EeYzytRNuXWQ4Al0VhUZNoC+1F30qMYwW1OvqRAXDN4DPFxo2zO1ia7bUX
|
||||
ofQvp9TAdEEGWOOVaLP0FHDiieKL/3iyjCAP+D55AoGBAMKi7ZkIBqzRhknwp13v
|
||||
iutRynDO8lXUeLxp/WhPj5Go5S7LbUxEwTlEikxAEdYqtCl7DMxWBgaDojxOahY6
|
||||
LBuK53LWCG9y06laGy8gxbNJ86VbjpwwTm1eQeryPg/CSRPVb4G5Rhe4hZJEMdv3
|
||||
Kt0IfWn4z2YLCtPtOrqMERSk
|
||||
-----END PRIVATE KEY-----
|
||||
@ -8,6 +8,8 @@ TARGET = nymea-remoteproxy-tests
|
||||
INCLUDEPATH += ../libnymea-remoteproxy
|
||||
LIBS += -L$$top_builddir/libnymea-remoteproxy/ -lnymea-remoteproxy
|
||||
|
||||
RESOURCES += certificate.qrc
|
||||
|
||||
HEADERS += nymea-remoteproxy-tests.h
|
||||
SOURCES += nymea-remoteproxy-tests.cpp
|
||||
|
||||
|
||||
Reference in New Issue
Block a user