mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Replace WebSocket implementation (#1819)
This commit is contained in:
parent
d16142d28e
commit
0aa7df47d3
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
||||
if: ${{ matrix.language == 'cpp' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes git build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev libftdi1-dev
|
||||
sudo apt-get install --yes git build-essential qtbase5-dev libqt5serialport5-dev libqt5websockets5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev libftdi1-dev
|
||||
|
||||
- name: Temporarily downgrade CMake to 3.28.3 # Please remove if GitHub has updated Cmake (greater than 3.30.0)
|
||||
uses: jwlawson/actions-setup-cmake@v2
|
||||
|
4
.github/workflows/qt5_6.yml
vendored
4
.github/workflows/qt5_6.yml
vendored
@ -191,9 +191,9 @@ jobs:
|
||||
- name: 📥 Install Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: ${{ inputs.qt_version == '6' && '6.7' || '5.15.*' }}
|
||||
version: ${{ inputs.qt_version == '6' && '6.8' || '5.15.*' }}
|
||||
target: 'desktop'
|
||||
modules: ${{ inputs.qt_version == '6' && 'qtserialport' || '' }}
|
||||
modules: ${{ inputs.qt_version == '6' && 'qtserialport qtwebsockets' || '' }}
|
||||
cache: 'true'
|
||||
cache-key-prefix: 'cache-qt-windows'
|
||||
|
||||
|
@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Fixed: Python 3.12 crashes (#1747)
|
||||
- osX Grabber: Use ScreenCaptureKit under macOS 15 and above
|
||||
- Removed maximum LED number constraint from Matrix layout schema which was not synced with the UI behaviour (#1804)
|
||||
- Fixed bespoke WebSocket implementation by using of QWebSockets (#1816, #1448, #1247, #1130)
|
||||
|
||||
**JSON-API**
|
||||
- Refactored JSON-API to ensure consistent authorization behaviour across sessions and single requests with token authorization.
|
||||
|
@ -570,7 +570,7 @@ message(STATUS "Found Qt Version: ${QT_VERSION}")
|
||||
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
||||
set(QT_MIN_VERSION "6.2.2")
|
||||
else()
|
||||
set(QT_MIN_VERSION "5.5.0")
|
||||
set(QT_MIN_VERSION "5.9.0")
|
||||
endif()
|
||||
|
||||
if("${QT_VERSION}" VERSION_LESS "${QT_MIN_VERSION}")
|
||||
|
@ -30,16 +30,16 @@ wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/
|
||||
|
||||
### Ubuntu
|
||||
|
||||
**amd64 (Jammy):**
|
||||
**amd64 (Noble Numbat):**
|
||||
```console
|
||||
wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh --name jammy
|
||||
wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh --name noble
|
||||
```
|
||||
|
||||
### Fedora
|
||||
|
||||
**amd64 (39):**
|
||||
**amd64 (41):**
|
||||
```console
|
||||
wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh --name 39
|
||||
wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh --name 41
|
||||
```
|
||||
|
||||
## Cross compilation on amd64 for developers
|
||||
@ -61,14 +61,14 @@ cd $HYPERION_HOME
|
||||
|
||||
```console
|
||||
sudo apt-get update
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev libftdi1-dev
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5websockets5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev libftdi1-dev
|
||||
```
|
||||
|
||||
**Ubuntu (22.04+) - Qt6 based**
|
||||
|
||||
```console
|
||||
sudo apt-get update
|
||||
sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libxkbcommon-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config libftdi1-dev
|
||||
sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libqt6websockets6-dev libxkbcommon-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config libftdi1-dev
|
||||
```
|
||||
|
||||
**For Linux X11/XCB grabber support**
|
||||
@ -110,7 +110,7 @@ See [AUR](https://aur.archlinux.org/packages/?O=0&SeB=nd&K=hyperion&outdated=&SB
|
||||
The following dependencies are needed to build hyperion.ng on fedora.
|
||||
```console
|
||||
sudo dnf -y groupinstall "Development Tools"
|
||||
sudo dnf install python3-devel qt-devel qt5-qtbase-devel qt5-qtserialport-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel alsa-lib-devel turbojpeg-devel libusb-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel libftdi1-dev
|
||||
sudo dnf install python3-devel qt-devel qt6-qtbase-devel qt6-qtserialport-devel qt6-qtwebsockets-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel alsa-lib-devel turbojpeg-devel libusb-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel libftdi1-dev
|
||||
```
|
||||
After installing the dependencies, you can continue with the compile instructions later on this page (the more detailed way..).
|
||||
|
||||
|
@ -932,13 +932,17 @@ void JsonAPI::handleSchemaGetCommand(const QJsonObject& /*message*/, const JsonA
|
||||
// Add infor about the type of setting elements
|
||||
QJsonObject settingTypes;
|
||||
QJsonArray globalSettingTypes;
|
||||
for (const QString &type : SettingsTable().getGlobalSettingTypes()) {
|
||||
|
||||
SettingsTable settingsTable;
|
||||
for (const QString &type : settingsTable.getGlobalSettingTypes())
|
||||
{
|
||||
globalSettingTypes.append(type);
|
||||
}
|
||||
settingTypes.insert("globalProperties", globalSettingTypes);
|
||||
|
||||
QJsonArray instanceSettingTypes;
|
||||
for (const QString &type : SettingsTable().getInstanceSettingTypes()) {
|
||||
for (const QString &type : settingsTable.getInstanceSettingTypes())
|
||||
{
|
||||
instanceSettingTypes.append(type);
|
||||
}
|
||||
settingTypes.insert("instanceProperties", instanceSettingTypes);
|
||||
|
@ -67,12 +67,12 @@ int ProviderFtdi::open()
|
||||
|
||||
Debug(_log, "Opening FTDI device=%s", QSTRING_CSTR(_deviceName));
|
||||
|
||||
FTDI_CHECK_RESULT((rc = ftdi_usb_open_string(_ftdic, QSTRING_CSTR(_deviceName))) < 0);
|
||||
FTDI_CHECK_RESULT((rc = ftdi_usb_open_string(_ftdic, QSTRING_CSTR(_deviceName))) < 0)
|
||||
/* doing this disable resets things if they were in a bad state */
|
||||
FTDI_CHECK_RESULT((rc = ftdi_disable_bitbang(_ftdic)) < 0);
|
||||
FTDI_CHECK_RESULT((rc = ftdi_setflowctrl(_ftdic, SIO_DISABLE_FLOW_CTRL)) < 0);
|
||||
FTDI_CHECK_RESULT((rc = ftdi_set_bitmode(_ftdic, 0x00, BITMODE_RESET)) < 0);
|
||||
FTDI_CHECK_RESULT((rc = ftdi_set_bitmode(_ftdic, 0xff, BITMODE_MPSSE)) < 0);
|
||||
FTDI_CHECK_RESULT((rc = ftdi_disable_bitbang(_ftdic)) < 0)
|
||||
FTDI_CHECK_RESULT((rc = ftdi_setflowctrl(_ftdic, SIO_DISABLE_FLOW_CTRL)) < 0)
|
||||
FTDI_CHECK_RESULT((rc = ftdi_set_bitmode(_ftdic, 0x00, BITMODE_RESET)) < 0)
|
||||
FTDI_CHECK_RESULT((rc = ftdi_set_bitmode(_ftdic, 0xff, BITMODE_MPSSE)) < 0)
|
||||
|
||||
double reference_clock = 60e6;
|
||||
int divisor = (reference_clock / 2 / _baudRate_Hz) - 1;
|
||||
@ -86,7 +86,7 @@ int ProviderFtdi::open()
|
||||
pinDirection
|
||||
};
|
||||
|
||||
FTDI_CHECK_RESULT((rc = ftdi_write_data(_ftdic, buf.data(), buf.size())) != buf.size());
|
||||
FTDI_CHECK_RESULT(static_cast<size_t>(rc = ftdi_write_data(_ftdic, buf.data(), static_cast<int>(buf.size()))) != buf.size())
|
||||
|
||||
_isDeviceReady = true;
|
||||
return rc;
|
||||
@ -134,7 +134,7 @@ int ProviderFtdi::writeBytes(const qint64 size, const uint8_t *data)
|
||||
// SET_BITS_LOW takes 2 arguments, so we're inserting data in -3 position from the end
|
||||
buf.insert(buf.end() - 3, &data[0], &data[size]);
|
||||
|
||||
FTDI_CHECK_RESULT((rc = ftdi_write_data(_ftdic, buf.data(), buf.size())) != buf.size());
|
||||
FTDI_CHECK_RESULT(static_cast<size_t>(rc = ftdi_write_data(_ftdic, buf.data(), static_cast<int>(buf.size()))) != buf.size())
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ QJsonObject ProviderFtdi::discover(const QJsonObject & /*params*/)
|
||||
struct ftdi_device_list *curdev = devlist;
|
||||
QMap<QString, uint8_t> deviceIndexes;
|
||||
|
||||
while (curdev)
|
||||
while (curdev != nullptr)
|
||||
{
|
||||
libusb_device_descriptor desc;
|
||||
int rc = libusb_get_device_descriptor(curdev->dev, &desc);
|
||||
@ -161,8 +161,7 @@ QJsonObject ProviderFtdi::discover(const QJsonObject & /*params*/)
|
||||
QString vendorIdentifier = QString("0x%1").arg(desc.idVendor, 4, 16, QChar{'0'});
|
||||
QString productIdentifier = QString("0x%1").arg(desc.idProduct, 4, 16, QChar{'0'});
|
||||
QString vendorAndProduct = QString("%1:%2")
|
||||
.arg(vendorIdentifier)
|
||||
.arg(productIdentifier);
|
||||
.arg(vendorIdentifier,productIdentifier);
|
||||
uint8_t deviceIndex = deviceIndexes.value(vendorAndProduct, 0);
|
||||
|
||||
char serial_string[128] = {0};
|
||||
@ -174,7 +173,7 @@ QJsonObject ProviderFtdi::discover(const QJsonObject & /*params*/)
|
||||
QString ftdiOpenString;
|
||||
if(!serialNumber.isEmpty())
|
||||
{
|
||||
ftdiOpenString = QString("s:%1:%2").arg(vendorAndProduct).arg(serialNumber);
|
||||
ftdiOpenString = QString("s:%1:%2").arg(vendorAndProduct, serialNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ PythonProgram::PythonProgram(const QString& name, Logger* log) :
|
||||
PyEval_RestoreThread(mainThreadState);
|
||||
_tstate = Py_NewInterpreter();
|
||||
#else
|
||||
PyThreadState* prev = PyThreadState_Swap(NULL);
|
||||
PyThreadState_Swap(NULL);
|
||||
|
||||
// Create a new interpreter configuration object
|
||||
PyInterpreterConfig config{};
|
||||
|
@ -50,16 +50,15 @@ void ImageResampler::processImage(const uint8_t * data, int width, int height, i
|
||||
|
||||
outputImage.resize(outputWidth, outputHeight);
|
||||
|
||||
int xDestStart, xDestEnd;
|
||||
int yDestStart, yDestEnd;
|
||||
int xDestStart {0};
|
||||
int xDestEnd = {outputWidth-1};
|
||||
int yDestStart = {0};
|
||||
int yDestEnd = {outputWidth-1};
|
||||
|
||||
switch (_flipMode)
|
||||
{
|
||||
case FlipMode::NO_CHANGE:
|
||||
xDestStart = 0;
|
||||
xDestEnd = outputWidth-1;
|
||||
yDestStart = 0;
|
||||
yDestEnd = outputHeight-1;
|
||||
//use the initalized values
|
||||
break;
|
||||
case FlipMode::HORIZONTAL:
|
||||
xDestStart = 0;
|
||||
|
@ -1,3 +1,4 @@
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS WebSockets REQUIRED)
|
||||
|
||||
file(GLOB_RECURSE webFiles RELATIVE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/assets/webconfig/*)
|
||||
file(RELATIVE_PATH webConfigPath ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/assets/webconfig)
|
||||
@ -28,13 +29,13 @@ add_library(webserver
|
||||
${CMAKE_SOURCE_DIR}/libsrc/webserver/StaticFileServing.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebJsonRpc.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebJsonRpc.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketClient.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketClient.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketUtils.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketJsonHandler.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketJsonHandler.cpp
|
||||
${CMAKE_BINARY_DIR}/WebConfig.qrc
|
||||
)
|
||||
)
|
||||
|
||||
target_link_libraries(webserver
|
||||
Qt${QT_VERSION_MAJOR}::WebSockets
|
||||
hyperion
|
||||
hyperion-utils
|
||||
hyperion-api
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "QtHttpReply.h"
|
||||
#include "QtHttpServer.h"
|
||||
#include "QtHttpHeader.h"
|
||||
#include "WebSocketClient.h"
|
||||
#include "WebSocketJsonHandler.h"
|
||||
#include "WebJsonRpc.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
@ -27,8 +27,9 @@ QtHttpClientWrapper::QtHttpClientWrapper (QTcpSocket * sock, const bool& localCo
|
||||
, m_localConnection(localConnection)
|
||||
, m_websocketClient(nullptr)
|
||||
, m_webJsonRpc (nullptr)
|
||||
, m_websocketServer (nullptr)
|
||||
{
|
||||
connect (m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
|
||||
connect(m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
|
||||
}
|
||||
|
||||
QString QtHttpClientWrapper::getGuid (void)
|
||||
@ -50,6 +51,11 @@ void QtHttpClientWrapper::onClientDataReceived (void)
|
||||
{
|
||||
if (m_sockClient != Q_NULLPTR)
|
||||
{
|
||||
if (!m_sockClient->isTransactionStarted())
|
||||
{
|
||||
m_sockClient->startTransaction();
|
||||
}
|
||||
|
||||
while (m_sockClient->bytesAvailable () != 0)
|
||||
{
|
||||
QByteArray line = m_sockClient->readLine ();
|
||||
@ -162,22 +168,30 @@ void QtHttpClientWrapper::onClientDataReceived (void)
|
||||
{
|
||||
case RequestParsed: // a valid request has ben fully parsed
|
||||
{
|
||||
// Catch websocket header "Upgrade"
|
||||
if(m_currentRequest->getHeader(QtHttpHeader::Upgrade).toLower() == "websocket")
|
||||
const auto& upgradeValue = m_currentRequest->getHeader(QtHttpHeader::Upgrade).toLower();
|
||||
if (upgradeValue == "websocket")
|
||||
{
|
||||
if(m_websocketClient == Q_NULLPTR)
|
||||
{
|
||||
// disconnect this slot from socket for further requests
|
||||
disconnect(m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
|
||||
// disabling packet bunching
|
||||
m_sockClient->setSocketOption(QAbstractSocket::LowDelayOption, 1);
|
||||
m_sockClient->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
|
||||
m_websocketClient = new WebSocketClient(m_currentRequest, m_sockClient, m_localConnection, this);
|
||||
m_sockClient->rollbackTransaction();
|
||||
|
||||
QString servername = QCoreApplication::applicationName() + QLatin1Char('/') + HYPERION_VERSION;
|
||||
QWebSocketServer::SslMode secureMode = m_serverHandle->isSecure() ? QWebSocketServer::SecureMode : QWebSocketServer::NonSecureMode;
|
||||
m_websocketServer.reset(new QWebSocketServer(servername, secureMode));
|
||||
connect(m_websocketServer.get(), &QWebSocketServer::newConnection,
|
||||
this, &QtHttpClientWrapper::onNewWebSocketConnection);
|
||||
|
||||
m_websocketServer->handleConnection(m_sockClient);
|
||||
emit m_sockClient->readyRead();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
m_sockClient->commitTransaction();
|
||||
// add post data to request and catch /jsonrpc subroute url
|
||||
if ( m_currentRequest->getCommand() == "POST")
|
||||
{
|
||||
@ -227,6 +241,8 @@ void QtHttpClientWrapper::onClientDataReceived (void)
|
||||
case ParsingError: // there was an error durin one of parsing steps
|
||||
{
|
||||
m_sockClient->readAll (); // clear remaining buffer to ignore content
|
||||
m_sockClient->commitTransaction();
|
||||
|
||||
QtHttpReply reply (m_serverHandle);
|
||||
reply.setStatusCode (QtHttpReply::BadRequest);
|
||||
reply.appendRawData (QByteArrayLiteral ("<h1>Bad Request (HTTP parsing error) !</h1>"));
|
||||
@ -365,3 +381,17 @@ void QtHttpClientWrapper::closeConnection()
|
||||
}
|
||||
m_sockClient->close ();
|
||||
}
|
||||
|
||||
void QtHttpClientWrapper::onNewWebSocketConnection() {
|
||||
|
||||
// Handle the pending connection
|
||||
QWebSocket* webSocket = m_websocketServer->nextPendingConnection();
|
||||
if (webSocket) {
|
||||
// Manage the WebSocketJsonHandler for this connection
|
||||
WebSocketJsonHandler* handler = new WebSocketJsonHandler(webSocket);
|
||||
connect(webSocket, &QWebSocket::disconnected, handler, &QObject::deleteLater);
|
||||
}
|
||||
else {
|
||||
qWarning() << "No pending WebSocket connection!";
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,9 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QWebSocketServer>
|
||||
#include <QCoreApplication>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class QTcpSocket;
|
||||
|
||||
@ -39,8 +42,12 @@ public:
|
||||
///
|
||||
void closeConnection();
|
||||
|
||||
signals:
|
||||
void newWebSocketConnection();
|
||||
|
||||
private slots:
|
||||
void onClientDataReceived (void);
|
||||
void onNewWebSocketConnection();
|
||||
|
||||
protected:
|
||||
ParsingStatus sendReplyToClient (QtHttpReply * reply);
|
||||
@ -59,6 +66,7 @@ private:
|
||||
WebSocketClient * m_websocketClient;
|
||||
WebJsonRpc * m_webJsonRpc;
|
||||
QByteArray m_fragment;
|
||||
QScopedPointer<QWebSocketServer> m_websocketServer;
|
||||
};
|
||||
|
||||
#endif // QTHTTPCLIENTWRAPPER_H
|
||||
|
@ -40,7 +40,7 @@ QtHttpServer::QtHttpServer (QObject * parent)
|
||||
, m_netOrigin (NetOrigin::getInstance())
|
||||
{
|
||||
m_sockServer = new QtHttpServerWrapper (this);
|
||||
connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected);
|
||||
connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
void QtHttpServer::start (quint16 port)
|
||||
@ -81,27 +81,27 @@ void QtHttpServer::onClientConnected (void)
|
||||
{
|
||||
if (QTcpSocket * sock = m_sockServer->nextPendingConnection ())
|
||||
{
|
||||
if(m_netOrigin->accessAllowed(sock->peerAddress(), sock->localAddress()))
|
||||
if (m_netOrigin->accessAllowed(sock->peerAddress(), sock->localAddress()))
|
||||
{
|
||||
connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
|
||||
connect(sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
|
||||
|
||||
if (m_useSsl)
|
||||
{
|
||||
if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock))
|
||||
if (QSslSocket* ssl = qobject_cast<QSslSocket*> (sock))
|
||||
{
|
||||
connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
|
||||
connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
|
||||
connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
|
||||
connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
|
||||
ssl->setLocalCertificateChain (m_sslCerts);
|
||||
ssl->setPrivateKey (m_sslKey);
|
||||
ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer);
|
||||
ssl->startServerEncryption ();
|
||||
connect(ssl, SslErrorSignal(&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
|
||||
connect(ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
|
||||
connect(ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
|
||||
connect(ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
|
||||
ssl->setLocalCertificateChain(m_sslCerts);
|
||||
ssl->setPrivateKey(m_sslKey);
|
||||
ssl->setPeerVerifyMode(QSslSocket::AutoVerifyPeer);
|
||||
ssl->startServerEncryption();
|
||||
}
|
||||
}
|
||||
|
||||
QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this);
|
||||
m_socksClientsHash.insert (sock, wrapper);
|
||||
QtHttpClientWrapper* wrapper = new QtHttpClientWrapper(sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this);
|
||||
m_socksClientsHash.insert(sock, wrapper);
|
||||
emit clientConnected (wrapper->getGuid ());
|
||||
}
|
||||
else
|
||||
|
@ -46,21 +46,22 @@ public:
|
||||
|
||||
typedef void (QSslSocket::* SslErrorSignal) (const QList<QSslError> &);
|
||||
|
||||
const QString & getServerName (void) const { return m_serverName; };
|
||||
const QString & getServerName (void) const { return m_serverName; }
|
||||
|
||||
quint16 getServerPort (void) const { return m_sockServer->serverPort(); };
|
||||
QString getErrorString (void) const { return m_sockServer->errorString(); };
|
||||
bool isListening() { return m_sockServer->isListening(); };
|
||||
quint16 getServerPort (void) const { return m_sockServer->serverPort(); }
|
||||
QString getErrorString (void) const { return m_sockServer->errorString(); }
|
||||
bool isListening() { return m_sockServer->isListening(); }
|
||||
bool isSecure() { return m_useSsl; }
|
||||
|
||||
public slots:
|
||||
void start (quint16 port = 0);
|
||||
void stop (void);
|
||||
void setUseSecure (const bool ssl = true);
|
||||
void setServerName (const QString & serverName) { m_serverName = serverName; };
|
||||
void setPrivateKey (const QSslKey & key) { m_sslKey = key; };
|
||||
void setCertificates (const QList<QSslCertificate> & certs) { m_sslCerts = certs; };
|
||||
QSslKey getPrivateKey() { return m_sslKey; };
|
||||
QList<QSslCertificate> getCertificates() { return m_sslCerts; };
|
||||
void setUseSecure (bool ssl = true);
|
||||
void setServerName (const QString & serverName) { m_serverName = serverName; }
|
||||
void setPrivateKey (const QSslKey & key) { m_sslKey = key; }
|
||||
void setCertificates (const QList<QSslCertificate> & certs) { m_sslCerts = certs; }
|
||||
QSslKey getPrivateKey() { return m_sslKey; }
|
||||
QList<QSslCertificate> getCertificates() { return m_sslCerts; }
|
||||
|
||||
signals:
|
||||
void started (quint16 port);
|
||||
@ -73,10 +74,10 @@ signals:
|
||||
private slots:
|
||||
void onClientConnected (void);
|
||||
void onClientDisconnected (void);
|
||||
void onClientSslEncrypted (void) { };
|
||||
void onClientSslPeerVerifyError (const QSslError & err) { Q_UNUSED (err) };
|
||||
void onClientSslErrors (const QList<QSslError> & errors) { Q_UNUSED (errors) };
|
||||
void onClientSslModeChanged (QSslSocket::SslMode mode) { Q_UNUSED (mode) };
|
||||
void onClientSslEncrypted (void) { }
|
||||
void onClientSslPeerVerifyError (const QSslError & err) { Q_UNUSED (err) }
|
||||
void onClientSslErrors (const QList<QSslError> & errors) { Q_UNUSED (errors) }
|
||||
void onClientSslModeChanged (QSslSocket::SslMode mode) { Q_UNUSED (mode) }
|
||||
|
||||
private:
|
||||
bool m_useSsl;
|
||||
|
@ -1,359 +0,0 @@
|
||||
#include "WebSocketClient.h"
|
||||
#include "QtHttpRequest.h"
|
||||
#include "QtHttpHeader.h"
|
||||
|
||||
#include <hyperion/Hyperion.h>
|
||||
#include <api/JsonAPI.h>
|
||||
#include <api/JsonCallbacks.h>
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QtEndian>
|
||||
#include <QCryptographicHash>
|
||||
#include <QJsonObject>
|
||||
|
||||
WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, bool localConnection, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _socket(sock)
|
||||
, _log(Logger::getInstance("WEBSOCKET"))
|
||||
{
|
||||
// connect socket; disconnect handled from QtHttpServer
|
||||
connect(_socket, &QTcpSocket::readyRead , this, &WebSocketClient::handleWebSocketFrame);
|
||||
|
||||
// QtHttpRequest contains all headers for handshake
|
||||
QByteArray secWebSocketKey = request->getHeader(QtHttpHeader::SecWebSocketKey);
|
||||
const QString client = request->getClientInfo().clientAddress.toString();
|
||||
|
||||
// Json processor
|
||||
_jsonAPI.reset(new JsonAPI(client, _log, localConnection, this));
|
||||
connect(_jsonAPI.get(), &JsonAPI::callbackReady, this, &WebSocketClient::sendMessage);
|
||||
connect(_jsonAPI.get(), &JsonAPI::forceClose, this,[this]() { this->sendClose(CLOSECODE::NORMAL); });
|
||||
|
||||
connect(_jsonAPI->getCallBack().get(), &JsonCallbacks::callbackReady, this, &WebSocketClient::sendMessage);
|
||||
|
||||
connect(this, &WebSocketClient::handleMessage, _jsonAPI.get(), &JsonAPI::handleMessage);
|
||||
|
||||
Debug(_log, "New connection from %s", QSTRING_CSTR(client));
|
||||
|
||||
// do handshake
|
||||
secWebSocketKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
QByteArray hash = QCryptographicHash::hash(secWebSocketKey, QCryptographicHash::Sha1).toBase64();
|
||||
|
||||
QString data
|
||||
= QString("HTTP/1.1 101 Switching Protocols\r\n")
|
||||
+ QString("Upgrade: websocket\r\n")
|
||||
+ QString("Connection: Upgrade\r\n")
|
||||
+ QString("Sec-WebSocket-Accept: ")+QString(hash.data()) + "\r\n\r\n";
|
||||
|
||||
_socket->write(QSTRING_CSTR(data), data.size());
|
||||
_socket->flush();
|
||||
|
||||
// Init JsonAPI
|
||||
_jsonAPI->initialize();
|
||||
}
|
||||
|
||||
void WebSocketClient::handleWebSocketFrame()
|
||||
{
|
||||
while (_socket->bytesAvailable())
|
||||
{
|
||||
// we are on no continious reading from socket from call before
|
||||
if (!_notEnoughData)
|
||||
{
|
||||
getWsFrameHeader(&_wsh);
|
||||
}
|
||||
|
||||
if(_socket->bytesAvailable() < (qint64)_wsh.payloadLength)
|
||||
{
|
||||
_notEnoughData=true;
|
||||
return;
|
||||
}
|
||||
_notEnoughData = false;
|
||||
|
||||
QByteArray buf = _socket->read(_wsh.payloadLength);
|
||||
|
||||
if (OPCODE::invalid((OPCODE::value)_wsh.opCode))
|
||||
{
|
||||
sendClose(CLOSECODE::INV_TYPE, "invalid opcode");
|
||||
return;
|
||||
}
|
||||
|
||||
// check the type of data frame
|
||||
bool isContinuation=false;
|
||||
|
||||
switch (_wsh.opCode)
|
||||
{
|
||||
case OPCODE::CONTINUATION:
|
||||
isContinuation = true;
|
||||
// no break here, just jump over to opcode text
|
||||
|
||||
case OPCODE::BINARY:
|
||||
case OPCODE::TEXT:
|
||||
{
|
||||
// A fragmented message consists of a single frame with the FIN bit
|
||||
// clear and an opcode other than 0, followed by zero or more frames
|
||||
// with the FIN bit clear and the opcode set to 0, and terminated by
|
||||
// a single frame with the FIN bit set and an opcode of 0.
|
||||
//
|
||||
// Store frame type given by first frame
|
||||
if (_wsh.opCode != OPCODE::CONTINUATION )
|
||||
{
|
||||
_frameOpCode = _wsh.opCode;
|
||||
}
|
||||
|
||||
// check for protocol violations
|
||||
if (_onContinuation && !isContinuation)
|
||||
{
|
||||
sendClose(CLOSECODE::VIOLATION, "protocol violation, somebody sends frames in between continued frames");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_wsh.masked && _wsh.opCode == OPCODE::TEXT)
|
||||
{
|
||||
sendClose(CLOSECODE::VIOLATION, "protocol violation, unmasked text frames not allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
// unmask data
|
||||
for (int i=0; i < buf.size(); i++)
|
||||
{
|
||||
buf[i] = buf[i] ^ _wsh.key[i % 4];
|
||||
}
|
||||
|
||||
_onContinuation = !_wsh.fin || isContinuation;
|
||||
|
||||
// frame contains text, extract it, append data if this is a continuation
|
||||
if (_wsh.fin && ! isContinuation) // one frame
|
||||
{
|
||||
_wsReceiveBuffer.clear();
|
||||
}
|
||||
_wsReceiveBuffer.append(buf);
|
||||
|
||||
// this is the final frame, decode and handle data
|
||||
if (_wsh.fin)
|
||||
{
|
||||
_onContinuation = false;
|
||||
|
||||
if (_frameOpCode == OPCODE::TEXT)
|
||||
{
|
||||
emit handleMessage(QString(_wsReceiveBuffer),"");
|
||||
}
|
||||
else
|
||||
{
|
||||
handleBinaryMessage(_wsReceiveBuffer);
|
||||
}
|
||||
_wsReceiveBuffer.clear();
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE::CLOSE:
|
||||
{
|
||||
sendClose(CLOSECODE::NORMAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE::PING:
|
||||
{
|
||||
// ping received, send pong
|
||||
quint8 pong[] = {OPCODE::PONG, 0};
|
||||
_socket->write((const char*)pong, 2);
|
||||
_socket->flush();
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE::PONG:
|
||||
{
|
||||
Error(_log, "pong received, protocol violation!");
|
||||
}
|
||||
|
||||
default:
|
||||
Warning(_log, "strange %d\n%s\n", _wsh.opCode, QSTRING_CSTR(QString(buf)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::getWsFrameHeader(WebSocketHeader* header)
|
||||
{
|
||||
char fin_rsv_opcode, mask_length;
|
||||
_socket->getChar(&fin_rsv_opcode);
|
||||
_socket->getChar(&mask_length);
|
||||
|
||||
header->fin = (fin_rsv_opcode & BHB0_FIN) == BHB0_FIN;
|
||||
header->opCode = fin_rsv_opcode & BHB0_OPCODE;
|
||||
header->masked = (mask_length & BHB1_MASK) == BHB1_MASK;
|
||||
header->payloadLength = mask_length & BHB1_PAYLOAD;
|
||||
|
||||
// get size of payload
|
||||
switch (header->payloadLength)
|
||||
{
|
||||
case payload_size_code_16bit:
|
||||
{
|
||||
QByteArray buf = _socket->read(2);
|
||||
header->payloadLength = ((buf.at(0) << 8) & 0xFF00) | (buf.at(1) & 0xFF);
|
||||
}
|
||||
break;
|
||||
|
||||
case payload_size_code_64bit:
|
||||
{
|
||||
QByteArray buf = _socket->read(8);
|
||||
header->payloadLength = 0;
|
||||
for (uint i=0; i < 8; i++)
|
||||
{
|
||||
header->payloadLength |= ((quint64)(buf.at(i) & 0xFF)) << (8*(7-i));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// if the data is masked we need to get the key for unmasking
|
||||
if (header->masked)
|
||||
{
|
||||
_socket->read(header->key, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/// See http://tools.ietf.org/html/rfc6455#section-5.2 for more information
|
||||
void WebSocketClient::sendClose(int status, const QString& reason)
|
||||
{
|
||||
Debug(_log, "Send close to %s: %d %s", QSTRING_CSTR(_socket->peerAddress().toString()), status, QSTRING_CSTR(reason));
|
||||
ErrorIf(!reason.isEmpty(), _log, "%s", QSTRING_CSTR(reason));
|
||||
_receiveBuffer.clear();
|
||||
QByteArray sendBuffer;
|
||||
|
||||
sendBuffer.append(136+(status-1000));
|
||||
int length = reason.size();
|
||||
if(length >= 126)
|
||||
{
|
||||
sendBuffer.append( (length > 0xffff) ? 127 : 126);
|
||||
int num_bytes = (length > 0xffff) ? 8 : 2;
|
||||
|
||||
for(int c = num_bytes - 1; c != -1; c--)
|
||||
{
|
||||
sendBuffer.append( quint8((static_cast<unsigned long long>(length) >> (8 * c)) % 256));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sendBuffer.append(quint8(length));
|
||||
}
|
||||
|
||||
sendBuffer.append(reason.toUtf8());
|
||||
|
||||
_socket->write(sendBuffer);
|
||||
_socket->flush();
|
||||
_socket->close();
|
||||
}
|
||||
|
||||
|
||||
void WebSocketClient::handleBinaryMessage(QByteArray &data)
|
||||
{
|
||||
unsigned imgSize = data.size() - 4;
|
||||
unsigned width = ((data.at(2) << 8) & 0xFF00) | (data.at(3) & 0xFF);
|
||||
unsigned height = imgSize / width;
|
||||
|
||||
if ( imgSize % width > 0 )
|
||||
{
|
||||
Error(_log, "data size is not multiple of width");
|
||||
return;
|
||||
}
|
||||
|
||||
Image<ColorRgb> image;
|
||||
image.resize(width, height);
|
||||
|
||||
memcpy(image.memptr(), data.data()+4, imgSize);
|
||||
}
|
||||
|
||||
|
||||
qint64 WebSocketClient::sendMessage(QJsonObject obj)
|
||||
{
|
||||
QJsonDocument writer(obj);
|
||||
QByteArray data = writer.toJson(QJsonDocument::Compact) + "\n";
|
||||
|
||||
if (!_socket || (_socket->state() != QAbstractSocket::ConnectedState)) return 0;
|
||||
|
||||
qint64 payloadWritten = 0;
|
||||
quint32 payloadSize = data.size();
|
||||
const char * payload = data.data();
|
||||
|
||||
qint32 numFrames = payloadSize / FRAME_SIZE_IN_BYTES + ((quint64(payloadSize) % FRAME_SIZE_IN_BYTES) > 0 ? 1 : 0);
|
||||
|
||||
for (int i = 0; i < numFrames; ++i)
|
||||
{
|
||||
const bool isLastFrame = (i == (numFrames - 1));
|
||||
|
||||
quint64 position = i * FRAME_SIZE_IN_BYTES;
|
||||
quint32 frameSize = (payloadSize-position >= FRAME_SIZE_IN_BYTES) ? FRAME_SIZE_IN_BYTES : (payloadSize-position);
|
||||
|
||||
QByteArray buf = makeFrameHeader((i == 0) ? OPCODE::TEXT : OPCODE::CONTINUATION, frameSize, isLastFrame);
|
||||
sendMessage_Raw(buf);
|
||||
|
||||
qint64 written = sendMessage_Raw(payload+position,frameSize);
|
||||
if (written > 0)
|
||||
{
|
||||
payloadWritten += written;
|
||||
}
|
||||
else
|
||||
{
|
||||
_socket->flush();
|
||||
Error(_log, "Error writing bytes to socket: %s", QSTRING_CSTR(_socket->errorString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (payloadSize != payloadWritten)
|
||||
{
|
||||
Error(_log, "Error writing bytes to socket %d bytes from %d written", payloadWritten, payloadSize);
|
||||
return -1;
|
||||
}
|
||||
return payloadWritten;
|
||||
}
|
||||
|
||||
qint64 WebSocketClient::sendMessage_Raw(const char* data, quint64 size)
|
||||
{
|
||||
return _socket->write(data, size);
|
||||
}
|
||||
|
||||
qint64 WebSocketClient::sendMessage_Raw(QByteArray &data)
|
||||
{
|
||||
return _socket->write(data.data(), data.size());
|
||||
}
|
||||
|
||||
|
||||
QByteArray WebSocketClient::makeFrameHeader(quint8 opCode, quint64 payloadLength, bool lastFrame)
|
||||
{
|
||||
QByteArray header;
|
||||
|
||||
if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL)
|
||||
{
|
||||
//FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero)
|
||||
quint8 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));
|
||||
header.append(static_cast<char>(byte));
|
||||
|
||||
byte = 0x00;
|
||||
if (payloadLength <= 125)
|
||||
{
|
||||
byte |= static_cast<quint8>(payloadLength);
|
||||
header.append(static_cast<char>(byte));
|
||||
}
|
||||
else if (payloadLength <= 0xFFFFU)
|
||||
{
|
||||
byte |= 126;
|
||||
header.append(static_cast<char>(byte));
|
||||
quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
|
||||
header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte |= 127;
|
||||
header.append(static_cast<char>(byte));
|
||||
quint64 swapped = qToBigEndian<quint64>(payloadLength);
|
||||
header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Payload too big!");
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include "WebSocketUtils.h"
|
||||
#include <api/JsonAPI.h>
|
||||
|
||||
#include <QScopedPointer>
|
||||
|
||||
class QTcpSocket;
|
||||
|
||||
class QtHttpRequest;
|
||||
class Hyperion;
|
||||
class JsonAPI;
|
||||
|
||||
class WebSocketClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, bool localConnection, QObject* parent);
|
||||
|
||||
struct WebSocketHeader
|
||||
{
|
||||
bool fin;
|
||||
quint8 opCode;
|
||||
bool masked;
|
||||
quint64 payloadLength;
|
||||
char key[4];
|
||||
};
|
||||
|
||||
private:
|
||||
QTcpSocket* _socket;
|
||||
Logger* _log;
|
||||
Hyperion* _hyperion;
|
||||
QScopedPointer<JsonAPI> _jsonAPI;
|
||||
|
||||
void getWsFrameHeader(WebSocketHeader* header);
|
||||
void sendClose(int status, const QString& reason = "");
|
||||
void handleBinaryMessage(QByteArray &data);
|
||||
qint64 sendMessage_Raw(const char* data, quint64 size);
|
||||
qint64 sendMessage_Raw(QByteArray &data);
|
||||
QByteArray makeFrameHeader(quint8 opCode, quint64 payloadLength, bool lastFrame);
|
||||
|
||||
/// The buffer used for reading data from the socket
|
||||
QByteArray _receiveBuffer;
|
||||
|
||||
/// buffer for websockets multi frame receive
|
||||
QByteArray _wsReceiveBuffer;
|
||||
quint8 _maskKey[4];
|
||||
|
||||
bool _onContinuation = false;
|
||||
|
||||
// true when data is missing for parsing
|
||||
bool _notEnoughData = false;
|
||||
|
||||
// websocket header store
|
||||
WebSocketHeader _wsh;
|
||||
|
||||
//opCode of first frame (in case of fragmented frames)
|
||||
quint8 _frameOpCode;
|
||||
|
||||
// masks for fields in the basic header
|
||||
static uint8_t const BHB0_OPCODE = 0x0F;
|
||||
static uint8_t const BHB0_RSV3 = 0x10;
|
||||
static uint8_t const BHB0_RSV2 = 0x20;
|
||||
static uint8_t const BHB0_RSV1 = 0x40;
|
||||
static uint8_t const BHB0_FIN = 0x80;
|
||||
|
||||
static uint8_t const BHB1_PAYLOAD = 0x7F;
|
||||
static uint8_t const BHB1_MASK = 0x80;
|
||||
|
||||
static uint8_t const payload_size_code_16bit = 0x7E; // 126
|
||||
static uint8_t const payload_size_code_64bit = 0x7F; // 127
|
||||
|
||||
static const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //maximum size of a frame when sending a message
|
||||
|
||||
private slots:
|
||||
void handleWebSocketFrame();
|
||||
qint64 sendMessage(QJsonObject obj);
|
||||
|
||||
signals:
|
||||
void handleMessage(const QString &message, const QString &httpAuthHeader);
|
||||
};
|
63
libsrc/webserver/WebSocketJsonHandler.cpp
Normal file
63
libsrc/webserver/WebSocketJsonHandler.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#include "WebSocketJsonHandler.h"
|
||||
|
||||
#include <api/JsonAPI.h>
|
||||
#include <api/JsonCallbacks.h>
|
||||
#include <utils/JsonUtils.h>
|
||||
#include <utils/NetOrigin.h>
|
||||
|
||||
#define NO_TRACE_SEND
|
||||
#define NO_TRACE_RECEIVE
|
||||
|
||||
WebSocketJsonHandler::WebSocketJsonHandler(QWebSocket* websocket, QObject* parent)
|
||||
: QObject(parent)
|
||||
, _websocket(websocket)
|
||||
, _log(Logger::getInstance("WEBSOCKET"))
|
||||
{
|
||||
connect(_websocket, &QWebSocket::textMessageReceived, this, &WebSocketJsonHandler::onTextMessageReceived);
|
||||
connect(_websocket, &QWebSocket::binaryMessageReceived, this, &WebSocketJsonHandler::onBinaryMessageReceived);
|
||||
connect(_websocket, &QWebSocket::disconnected, this, &WebSocketJsonHandler::onDisconnected);
|
||||
|
||||
_peerAddress = _websocket->peerAddress().toString();
|
||||
_origin = websocket->origin();
|
||||
Debug(_log, "New WebSocket connection from %s initiated via: %s", QSTRING_CSTR(_peerAddress), QSTRING_CSTR(_origin));
|
||||
|
||||
bool localConnection = NetOrigin::getInstance()->isLocalAddress(_websocket->peerAddress(), _websocket->localAddress());
|
||||
|
||||
// Json processor
|
||||
_jsonAPI.reset(new JsonAPI(_peerAddress, _log, localConnection, this));
|
||||
|
||||
connect(_jsonAPI.get(), &JsonAPI::callbackReady, this, &WebSocketJsonHandler::sendMessage);
|
||||
connect(_jsonAPI->getCallBack().get(), &JsonCallbacks::callbackReady, this, &WebSocketJsonHandler::sendMessage);
|
||||
|
||||
// Init JsonAPI
|
||||
_jsonAPI->initialize();
|
||||
}
|
||||
|
||||
void WebSocketJsonHandler::onTextMessageReceived(const QString& message)
|
||||
{
|
||||
#ifdef RECEIVE_TRACE
|
||||
qDebug() << "[" << _peerAddress << "] WebSocket message received:" << message;
|
||||
#endif
|
||||
_jsonAPI->handleMessage(message);
|
||||
}
|
||||
|
||||
void WebSocketJsonHandler::onBinaryMessageReceived(const QByteArray& message)
|
||||
{
|
||||
#ifdef RECEIVE_TRACE
|
||||
qDebug() << "[" << _peerAddress << "] WebSocket message received:" << message.toHex();
|
||||
#endif
|
||||
Warning(_log,"Unexpected binary message received");
|
||||
}
|
||||
|
||||
qint64 WebSocketJsonHandler::sendMessage(QJsonObject obj)
|
||||
{
|
||||
#ifdef TRACE_SEND
|
||||
qDebug() << "[" << _peerAddress << "] WebSocket send message: " << obj;
|
||||
#endif
|
||||
return _websocket->sendTextMessage(JsonUtils::jsonValueToQString(obj));
|
||||
}
|
||||
|
||||
void WebSocketJsonHandler::onDisconnected()
|
||||
{
|
||||
Debug(_log, "WebSocket disconnected from %s initiated via: %s", QSTRING_CSTR(_peerAddress), QSTRING_CSTR(_origin));
|
||||
}
|
33
libsrc/webserver/WebSocketJsonHandler.h
Normal file
33
libsrc/webserver/WebSocketJsonHandler.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef WEBSOCKETJSONHANDLER_H
|
||||
#define WEBSOCKETJSONHANDLER_H
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <api/JsonAPI.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QWebSocket>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class WebSocketJsonHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WebSocketJsonHandler(QWebSocket* websocket, QObject* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void onTextMessageReceived(const QString& message);
|
||||
void onBinaryMessageReceived(const QByteArray& message);
|
||||
void onDisconnected();
|
||||
qint64 sendMessage(QJsonObject obj);
|
||||
|
||||
private:
|
||||
QWebSocket* _websocket;
|
||||
|
||||
Logger* _log;
|
||||
QScopedPointer<JsonAPI> _jsonAPI;
|
||||
QString _peerAddress;
|
||||
QString _origin;
|
||||
};
|
||||
|
||||
#endif // WEBSOCKETJSONHANDLER_H
|
@ -1,75 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/// Constants and utility functions related to WebSocket opcodes
|
||||
/**
|
||||
* WebSocket Opcodes are 4 bits. See RFC6455 section 5.2.
|
||||
*/
|
||||
namespace OPCODE
|
||||
{
|
||||
enum value
|
||||
{
|
||||
CONTINUATION = 0x0,
|
||||
TEXT = 0x1,
|
||||
BINARY = 0x2,
|
||||
RSV3 = 0x3,
|
||||
RSV4 = 0x4,
|
||||
RSV5 = 0x5,
|
||||
RSV6 = 0x6,
|
||||
RSV7 = 0x7,
|
||||
CLOSE = 0x8,
|
||||
PING = 0x9,
|
||||
PONG = 0xA,
|
||||
CONTROL_RSVB = 0xB,
|
||||
CONTROL_RSVC = 0xC,
|
||||
CONTROL_RSVD = 0xD,
|
||||
CONTROL_RSVE = 0xE,
|
||||
CONTROL_RSVF = 0xF
|
||||
};
|
||||
|
||||
/// Check if an opcode is reserved
|
||||
/**
|
||||
* @param v The opcode to test.
|
||||
* @return Whether or not the opcode is reserved.
|
||||
*/
|
||||
inline bool reserved(value v)
|
||||
{
|
||||
return (v >= RSV3 && v <= RSV7) || (v >= CONTROL_RSVB && v <= CONTROL_RSVF);
|
||||
}
|
||||
|
||||
/// Check if an opcode is invalid
|
||||
/**
|
||||
* Invalid opcodes are negative or require greater than 4 bits to store.
|
||||
*
|
||||
* @param v The opcode to test.
|
||||
* @return Whether or not the opcode is invalid.
|
||||
*/
|
||||
inline bool invalid(value v)
|
||||
{
|
||||
return (v > 0xF || v < 0);
|
||||
}
|
||||
|
||||
/// Check if an opcode is for a control frame
|
||||
/**
|
||||
* @param v The opcode to test.
|
||||
* @return Whether or not the opcode is a control opcode.
|
||||
*/
|
||||
inline bool is_control(value v)
|
||||
{
|
||||
return v >= 0x8;
|
||||
}
|
||||
}
|
||||
|
||||
namespace CLOSECODE
|
||||
{
|
||||
enum value
|
||||
{
|
||||
NORMAL = 1000,
|
||||
AWAY = 1001,
|
||||
TERM = 1002,
|
||||
INV_TYPE = 1003,
|
||||
INV_DATA = 1007,
|
||||
VIOLATION = 1008,
|
||||
BIG_MSG = 1009,
|
||||
UNEXPECTED= 1011
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user