mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Refactor websocket + true receive (#471)
* save it * ws: multiframe receive now works * port cfg write with autocorrect to jsonprocessor cleanup * cleanup * cleanup * add support for image data over ws binary frame
This commit is contained in:
parent
74ff5c7ada
commit
6f443a48dd
@ -421,7 +421,6 @@ $(document).ready(function() {
|
||||
|
||||
aceEdt.set(finalLedArray);
|
||||
$('#collapse4').collapse('show');
|
||||
|
||||
});
|
||||
|
||||
// create and update editor
|
||||
|
@ -97,6 +97,7 @@ function initWebSocket()
|
||||
case 1015: reason = "The connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified)."; break;
|
||||
default: reason = "Unknown reason";
|
||||
}
|
||||
console.log("[websocket::onclose] "+reason)
|
||||
$(hyperion).trigger({type:"close", reason:reason});
|
||||
watchdog = 10;
|
||||
connectionLostDetection();
|
||||
@ -262,15 +263,7 @@ function requestWriteConfig(config, full)
|
||||
});
|
||||
}
|
||||
|
||||
var config_str = escape(encode_utf8(JSON.stringify(serverConfig)));
|
||||
|
||||
$.post( "/cgi/cfg_set", { cfg: config_str })
|
||||
.done(function( data ) {
|
||||
$("html, body").animate({ scrollTop: 0 }, "slow");
|
||||
})
|
||||
.fail(function() {
|
||||
showInfoDialog('error', $.i18n('infoDialog_writeconf_error_title'), $.i18n('infoDialog_writeconf_error_text'));
|
||||
});
|
||||
sendToHyperion("config","setconfig", '"config":'+JSON.stringify(serverConfig));
|
||||
}
|
||||
|
||||
function requestWriteEffect(effectName,effectPy,effectArgs)
|
||||
|
@ -219,6 +219,12 @@ private:
|
||||
///
|
||||
void handleConfigGetCommand(const QJsonObject & message, const QString &command, const int tan);
|
||||
|
||||
/// Handle an incoming JSON SetConfig message from handleConfigCommand()
|
||||
///
|
||||
/// @param message the incoming message
|
||||
///
|
||||
void handleConfigSetCommand(const QJsonObject & message, const QString &command, const int tan);
|
||||
|
||||
///
|
||||
/// Handle an incoming JSON Component State message
|
||||
///
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Qt includes
|
||||
#include <QCryptographicHash>
|
||||
#include <QtEndian>
|
||||
#include <unistd.h>
|
||||
|
||||
// project includes
|
||||
#include "JsonClientConnection.h"
|
||||
@ -13,8 +14,11 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket)
|
||||
, _hyperion(Hyperion::getInstance())
|
||||
, _receiveBuffer()
|
||||
, _webSocketHandshakeDone(false)
|
||||
, _onContinuation(false)
|
||||
, _log(Logger::getInstance("JSONCLIENTCONNECTION"))
|
||||
, _notEnoughData(false)
|
||||
, _clientAddress(socket->peerAddress())
|
||||
, _connectionMode(CON_MODE::INIT)
|
||||
{
|
||||
// connect internal signals and slots
|
||||
connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
|
||||
@ -34,116 +38,182 @@ JsonClientConnection::~JsonClientConnection()
|
||||
|
||||
void JsonClientConnection::readData()
|
||||
{
|
||||
_receiveBuffer += _socket->readAll();
|
||||
switch(_connectionMode)
|
||||
{
|
||||
case CON_MODE::INIT:
|
||||
_receiveBuffer = _socket->readAll(); // initial read to determine connection
|
||||
_connectionMode = (_receiveBuffer.contains("Upgrade: websocket")) ? CON_MODE::WEBSOCKET : CON_MODE::RAW;
|
||||
|
||||
if (_webSocketHandshakeDone)
|
||||
{
|
||||
// websocket mode, data frame
|
||||
handleWebSocketFrame();
|
||||
}
|
||||
else
|
||||
{
|
||||
// might be a handshake request or raw socket data
|
||||
if(_receiveBuffer.contains("Upgrade: websocket"))
|
||||
{
|
||||
doWebSocketHandshake();
|
||||
} else
|
||||
{
|
||||
// raw socket data, handling as usual
|
||||
int bytes = _receiveBuffer.indexOf('\n') + 1;
|
||||
while(bytes > 0)
|
||||
// init websockets
|
||||
if (_connectionMode == CON_MODE::WEBSOCKET)
|
||||
{
|
||||
// create message string
|
||||
QString message(QByteArray(_receiveBuffer.data(), bytes));
|
||||
|
||||
// remove message data from buffer
|
||||
_receiveBuffer = _receiveBuffer.mid(bytes);
|
||||
|
||||
// handle message
|
||||
_jsonProcessor->handleMessage(message);
|
||||
|
||||
// try too look up '\n' again
|
||||
bytes = _receiveBuffer.indexOf('\n') + 1;
|
||||
doWebSocketHandshake();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if no ws, hand over the data to raw handling
|
||||
|
||||
case CON_MODE::RAW:
|
||||
handleRawJsonData();
|
||||
break;
|
||||
|
||||
case CON_MODE::WEBSOCKET:
|
||||
handleWebSocketFrame();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JsonClientConnection::handleRawJsonData()
|
||||
{
|
||||
_receiveBuffer += _socket->readAll();
|
||||
// raw socket data, handling as usual
|
||||
int bytes = _receiveBuffer.indexOf('\n') + 1;
|
||||
while(bytes > 0)
|
||||
{
|
||||
// create message string
|
||||
QString message(QByteArray(_receiveBuffer.data(), bytes));
|
||||
|
||||
// remove message data from buffer
|
||||
_receiveBuffer = _receiveBuffer.mid(bytes);
|
||||
|
||||
// handle message
|
||||
_jsonProcessor->handleMessage(message);
|
||||
|
||||
// try too look up '\n' again
|
||||
bytes = _receiveBuffer.indexOf('\n') + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonClientConnection::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(2) << 8) & 0xFF00) | (buf.at(3) & 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JsonClientConnection::handleWebSocketFrame()
|
||||
{
|
||||
if ((_receiveBuffer.at(0) & BHB0_FIN) == BHB0_FIN)
|
||||
//printf("frame\n");
|
||||
|
||||
// we are on no continious reading from socket from call before
|
||||
if (!_notEnoughData)
|
||||
{
|
||||
// final bit found, frame complete
|
||||
quint8 * maskKey = NULL;
|
||||
quint8 opCode = _receiveBuffer.at(0) & BHB0_OPCODE;
|
||||
bool isMasked = (_receiveBuffer.at(1) & BHB0_FIN) == BHB0_FIN;
|
||||
quint64 payloadLength = _receiveBuffer.at(1) & BHB1_PAYLOAD;
|
||||
quint32 index = 2;
|
||||
//printf("%ld\n", payloadLength);
|
||||
switch (payloadLength)
|
||||
{
|
||||
case payload_size_code_16bit:
|
||||
payloadLength = ((_receiveBuffer.at(2) << 8) & 0xFF00) | (_receiveBuffer.at(3) & 0xFF);
|
||||
index += 2;
|
||||
break;
|
||||
case payload_size_code_64bit:
|
||||
payloadLength = 0;
|
||||
for (uint i=0; i < 8; i++)
|
||||
{
|
||||
payloadLength |= ((quint64)(_receiveBuffer.at(index+i) & 0xFF)) << (8*(7-i));
|
||||
}
|
||||
index += 8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
getWsFrameHeader(&_wsh);
|
||||
}
|
||||
|
||||
if (isMasked)
|
||||
if(_socket->bytesAvailable() < (qint64)_wsh.payloadLength)
|
||||
{
|
||||
//printf("not enough data %llu %llu\n", _socket->bytesAvailable(), _wsh.payloadLength);
|
||||
_notEnoughData=true;
|
||||
return;
|
||||
}
|
||||
_notEnoughData = false;
|
||||
|
||||
QByteArray buf = _socket->read(_wsh.payloadLength);
|
||||
//printf("opcode %x payload bytes %llu avail: %llu\n", _wsh.opCode, _wsh.payloadLength, _socket->bytesAvailable());
|
||||
|
||||
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:
|
||||
{
|
||||
// if the data is masked we need to get the key for unmasking
|
||||
maskKey = new quint8[4];
|
||||
for (uint i=0; i < 4; i++)
|
||||
// check for protocal violations
|
||||
if (_onContinuation && !isContinuation)
|
||||
{
|
||||
maskKey[i] = _receiveBuffer.at(index + i);
|
||||
sendClose(CLOSECODE::VIOLATION, "protocol violation, somebody sends frames in between continued frames");
|
||||
return;
|
||||
}
|
||||
index += 4;
|
||||
}
|
||||
|
||||
// check the type of data frame
|
||||
switch (opCode)
|
||||
{
|
||||
case OPCODE::TEXT:
|
||||
if (!_wsh.masked && _wsh.opCode == OPCODE::TEXT)
|
||||
{
|
||||
// frame contains text, extract it
|
||||
QByteArray result = _receiveBuffer.mid(index, payloadLength);
|
||||
_receiveBuffer.clear();
|
||||
|
||||
// unmask data if necessary
|
||||
if (isMasked)
|
||||
{
|
||||
for (uint i=0; i < payloadLength; i++)
|
||||
{
|
||||
result[i] = (result[i] ^ maskKey[i % 4]);
|
||||
}
|
||||
if (maskKey != NULL)
|
||||
{
|
||||
delete[] maskKey;
|
||||
maskKey = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
_jsonProcessor->handleMessage(QString(result));
|
||||
sendClose(CLOSECODE::VIOLATION, "protocol violation, unmasked text frames not allowed");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
// 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 (_wsh.opCode == OPCODE::TEXT)
|
||||
{
|
||||
_jsonProcessor->handleMessage(QString(_wsReceiveBuffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
handleBinaryMessage(_wsReceiveBuffer);
|
||||
}
|
||||
_wsReceiveBuffer.clear();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE::CLOSE:
|
||||
{
|
||||
// close request, confirm
|
||||
quint8 close[] = {0x88, 0};
|
||||
_socket->write((const char*)close, 2);
|
||||
_socket->flush();
|
||||
_socket->close();
|
||||
sendClose(CLOSECODE::NORMAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE::PING:
|
||||
{
|
||||
// ping received, send pong
|
||||
@ -152,16 +222,47 @@ void JsonClientConnection::handleWebSocketFrame()
|
||||
_socket->flush();
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE::PONG:
|
||||
{
|
||||
Error(_log, "pong recievied, protocol violation!");
|
||||
}
|
||||
|
||||
default:
|
||||
Warning(_log, "strange %d\n%s\n", _wsh.opCode, QSTRING_CSTR(QString(buf)));
|
||||
}
|
||||
}
|
||||
|
||||
/// See http://tools.ietf.org/html/rfc6455#section-5.2 for more information
|
||||
void JsonClientConnection::sendClose(int status, QString reason)
|
||||
{
|
||||
Info(_log, "send close: %d %s", status, QSTRING_CSTR(reason));
|
||||
ErrorIf(!reason.isEmpty(), _log, 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
|
||||
{
|
||||
Error(_log, "Someone is sending very big messages over several frames... it's not supported yet");
|
||||
quint8 close[] = {0x88, 0};
|
||||
_socket->write((const char*)close, 2);
|
||||
_socket->flush();
|
||||
_socket->close();
|
||||
sendBuffer.append(quint8(length));
|
||||
}
|
||||
|
||||
sendBuffer.append(reason);
|
||||
|
||||
_socket->write(sendBuffer);
|
||||
_socket->flush();
|
||||
_socket->close();
|
||||
}
|
||||
|
||||
void JsonClientConnection::doWebSocketHandshake()
|
||||
@ -178,16 +279,14 @@ void JsonClientConnection::doWebSocketHandshake()
|
||||
value += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
// generate sha1 hash
|
||||
QByteArray hash = QCryptographicHash::hash(value, QCryptographicHash::Sha1);
|
||||
QByteArray hashB64 = hash.toBase64();
|
||||
QByteArray hash = QCryptographicHash::hash(value, QCryptographicHash::Sha1).toBase64();
|
||||
|
||||
// prepare an answer
|
||||
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(hashB64.data()) + "\r\n\r\n";
|
||||
+ QString("Sec-WebSocket-Accept: ")+QString(hash.data()) + "\r\n\r\n";
|
||||
|
||||
_socket->write(QSTRING_CSTR(data), data.size());
|
||||
_socket->flush();
|
||||
@ -202,12 +301,11 @@ void JsonClientConnection::socketClosed()
|
||||
emit connectionClosed(this);
|
||||
}
|
||||
|
||||
QByteArray JsonClientConnection::getFrameHeader(quint8 opCode, quint64 payloadLength, bool lastFrame)
|
||||
QByteArray JsonClientConnection::makeFrameHeader(quint8 opCode, quint64 payloadLength, bool lastFrame)
|
||||
{
|
||||
QByteArray header;
|
||||
bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
|
||||
|
||||
if (ok)
|
||||
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));
|
||||
@ -226,7 +324,7 @@ QByteArray JsonClientConnection::getFrameHeader(quint8 opCode, quint64 payloadLe
|
||||
quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
|
||||
header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
|
||||
}
|
||||
else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL)
|
||||
else
|
||||
{
|
||||
byte |= 127;
|
||||
header.append(static_cast<char>(byte));
|
||||
@ -258,7 +356,7 @@ qint64 JsonClientConnection::sendMessage_Raw(const char* data, quint64 size)
|
||||
return _socket->write(data, size);
|
||||
}
|
||||
|
||||
qint64 JsonClientConnection::sendMessage_Raw(QByteArray data)
|
||||
qint64 JsonClientConnection::sendMessage_Raw(QByteArray &data)
|
||||
{
|
||||
return _socket->write(data.data(), data.size());
|
||||
}
|
||||
@ -278,7 +376,8 @@ qint64 JsonClientConnection::sendMessage_Websockets(QByteArray &data)
|
||||
quint64 position = i * FRAME_SIZE_IN_BYTES;
|
||||
quint32 frameSize = (payloadSize-position >= FRAME_SIZE_IN_BYTES) ? FRAME_SIZE_IN_BYTES : (payloadSize-position);
|
||||
|
||||
sendMessage_Raw(getFrameHeader(OPCODE::TEXT, frameSize, isLastFrame));
|
||||
QByteArray buf = makeFrameHeader(OPCODE::TEXT, frameSize, isLastFrame);
|
||||
sendMessage_Raw(buf);
|
||||
qint64 written = sendMessage_Raw(payload+position,frameSize);
|
||||
if (written > 0)
|
||||
{
|
||||
@ -300,4 +399,24 @@ qint64 JsonClientConnection::sendMessage_Websockets(QByteArray &data)
|
||||
return payloadWritten;
|
||||
}
|
||||
|
||||
void JsonClientConnection::handleBinaryMessage(QByteArray &data)
|
||||
{
|
||||
uint8_t priority = data.at(0);
|
||||
unsigned duration_s = data.at(1);
|
||||
unsigned imgSize = data.size() - 4;
|
||||
unsigned width = ((data.at(2) << 8) & 0xFF00) | (data.at(3) & 0xFF);
|
||||
unsigned height = imgSize / width;
|
||||
|
||||
if ( ! (imgSize) % width)
|
||||
{
|
||||
Error(_log, "data size is not multiple of width");
|
||||
return;
|
||||
}
|
||||
|
||||
Image<ColorRgb> image;
|
||||
image.resize(width, height);
|
||||
|
||||
memcpy(image.memptr(), data.data()+4, imgSize);
|
||||
_hyperion->setImage(priority, image, duration_s*1000);
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,28 @@ namespace OPCODE {
|
||||
}
|
||||
}
|
||||
|
||||
namespace CLOSECODE {
|
||||
enum value {
|
||||
NORMAL = 1000,
|
||||
AWAY = 1001,
|
||||
TERM = 1002,
|
||||
INV_TYPE = 1003,
|
||||
INV_DATA = 1007,
|
||||
VIOLATION = 1008,
|
||||
BIG_MSG = 1009,
|
||||
UNEXPECTED= 1011
|
||||
};
|
||||
}
|
||||
|
||||
namespace CON_MODE {
|
||||
enum value {
|
||||
INIT = 0,
|
||||
RAW = 1,
|
||||
WEBSOCKET = 2
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// The Connection object created by \a JsonServer when a new connection is establshed
|
||||
///
|
||||
@ -88,6 +110,14 @@ public:
|
||||
///
|
||||
~JsonClientConnection();
|
||||
|
||||
struct WebSocketHeader
|
||||
{
|
||||
bool fin;
|
||||
quint8 opCode;
|
||||
bool masked;
|
||||
quint64 payloadLength;
|
||||
char key[4];
|
||||
};
|
||||
public slots:
|
||||
qint64 sendMessage(QJsonObject);
|
||||
|
||||
@ -124,11 +154,29 @@ private:
|
||||
///
|
||||
void handleWebSocketFrame();
|
||||
|
||||
QByteArray getFrameHeader(quint8 opCode, quint64 payloadLength, bool lastFrame);
|
||||
///
|
||||
/// Handle incoming raw data frame
|
||||
///
|
||||
void handleRawJsonData();
|
||||
|
||||
///
|
||||
/// create ws header from socket and decode it
|
||||
///
|
||||
QByteArray makeFrameHeader(quint8 opCode, quint64 payloadLength, bool lastFrame);
|
||||
|
||||
///
|
||||
/// handle binary message
|
||||
///
|
||||
/// This function should be placed elsewhere ....
|
||||
///
|
||||
void handleBinaryMessage(QByteArray &data);
|
||||
|
||||
qint64 sendMessage_Raw(const char* data, quint64 size);
|
||||
qint64 sendMessage_Raw(QByteArray data);
|
||||
qint64 sendMessage_Raw(QByteArray &data);
|
||||
qint64 sendMessage_Websockets(QByteArray &data);
|
||||
void sendClose(int status, QString reason = "");
|
||||
|
||||
void getWsFrameHeader(WebSocketHeader* header);
|
||||
|
||||
/// The TCP-Socket that is connected tot the Json-client
|
||||
QTcpSocket * _socket;
|
||||
@ -139,15 +187,25 @@ private:
|
||||
/// The buffer used for reading data from the socket
|
||||
QByteArray _receiveBuffer;
|
||||
|
||||
/// buffer for websockets multi frame receive
|
||||
QByteArray _wsReceiveBuffer;
|
||||
quint8 _maskKey[4];
|
||||
|
||||
/// used for WebSocket detection and connection handling
|
||||
bool _webSocketHandshakeDone;
|
||||
|
||||
bool _onContinuation;
|
||||
|
||||
/// The logger instance
|
||||
Logger * _log;
|
||||
|
||||
WebSocketHeader _wsh;
|
||||
bool _notEnoughData;
|
||||
|
||||
/// address of client
|
||||
QHostAddress _clientAddress;
|
||||
|
||||
CON_MODE::value _connectionMode;
|
||||
// masks for fields in the basic header
|
||||
static uint8_t const BHB0_OPCODE = 0x0F;
|
||||
static uint8_t const BHB0_RSV3 = 0x10;
|
||||
|
@ -10,7 +10,7 @@
|
||||
"subcommand": {
|
||||
"type" : "string",
|
||||
"required" : true,
|
||||
"enum" : ["getconfig","getschema","reload"]
|
||||
"enum" : ["setconfig","getconfig","getschema","reload"]
|
||||
},
|
||||
"tan" : {
|
||||
"type" : "integer"
|
||||
|
@ -795,10 +795,15 @@ void JsonProcessor::handleConfigCommand(const QJsonObject& message, const QStrin
|
||||
{
|
||||
QString subcommand = message["subcommand"].toString("");
|
||||
QString full_command = command + "-" + subcommand;
|
||||
|
||||
if (subcommand == "getschema")
|
||||
{
|
||||
handleSchemaGetCommand(message, full_command, tan);
|
||||
}
|
||||
else if (subcommand == "setconfig")
|
||||
{
|
||||
handleConfigSetCommand(message, full_command, tan);
|
||||
}
|
||||
else if (subcommand == "getconfig")
|
||||
{
|
||||
handleConfigGetCommand(message, full_command, tan);
|
||||
@ -815,6 +820,67 @@ void JsonProcessor::handleConfigCommand(const QJsonObject& message, const QStrin
|
||||
}
|
||||
}
|
||||
|
||||
void JsonProcessor::handleConfigSetCommand(const QJsonObject& message, const QString &command, const int tan)
|
||||
{
|
||||
if(message.size() > 0)
|
||||
{
|
||||
if (message.contains("config"))
|
||||
{
|
||||
QJsonObject hyperionConfigJsonObj = message["config"].toObject();
|
||||
try
|
||||
{
|
||||
Q_INIT_RESOURCE(resource);
|
||||
|
||||
QJsonObject schemaJson = QJsonFactory::readSchema(":/hyperion-schema");
|
||||
|
||||
QJsonSchemaChecker schemaChecker;
|
||||
schemaChecker.setSchema(schemaJson);
|
||||
|
||||
QPair<bool, bool> validate = schemaChecker.validate(hyperionConfigJsonObj);
|
||||
|
||||
if (validate.first && validate.second)
|
||||
{
|
||||
QJsonFactory::writeJson(_hyperion->getConfigFileName(), hyperionConfigJsonObj);
|
||||
}
|
||||
else if (!validate.first && validate.second)
|
||||
{
|
||||
Warning(_log,"Errors have been found in the configuration file. Automatic correction is applied");
|
||||
|
||||
QStringList schemaErrors = schemaChecker.getMessages();
|
||||
for (auto & schemaError : schemaErrors)
|
||||
Info(_log, QSTRING_CSTR(schemaError));
|
||||
|
||||
hyperionConfigJsonObj = schemaChecker.getAutoCorrectedConfig(hyperionConfigJsonObj);
|
||||
|
||||
if (!QJsonFactory::writeJson(_hyperion->getConfigFileName(), hyperionConfigJsonObj))
|
||||
throw std::runtime_error("ERROR: can not save configuration file, aborting");
|
||||
}
|
||||
else //Error in Schema
|
||||
{
|
||||
QString errorMsg = "ERROR: Json validation failed: \n";
|
||||
QStringList schemaErrors = schemaChecker.getMessages();
|
||||
for (auto & schemaError: schemaErrors)
|
||||
{
|
||||
Error(_log, "config write validation: %s", QSTRING_CSTR(schemaError));
|
||||
errorMsg += schemaError + "\n";
|
||||
}
|
||||
|
||||
throw std::runtime_error(errorMsg.toStdString());
|
||||
}
|
||||
sendSuccessReply(command, tan);
|
||||
}
|
||||
catch(const std::runtime_error& validate_error)
|
||||
{
|
||||
sendErrorReply("Error while validating json: " + QString(validate_error.what()), command, tan);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sendErrorReply("Error while parsing json: Message size " + QString(message.size()), command, tan);
|
||||
}
|
||||
}
|
||||
|
||||
void JsonProcessor::handleConfigGetCommand(const QJsonObject& message, const QString& command, const int tan)
|
||||
{
|
||||
// create result
|
||||
|
@ -37,15 +37,13 @@ void CgiHandler::exec(const QStringList & args, QtHttpRequest * request, QtHttpR
|
||||
_request = request;
|
||||
_reply = reply;
|
||||
cmd_cfg_jsonserver();
|
||||
cmd_cfg_get();
|
||||
cmd_cfg_set();
|
||||
// cmd_cfg_set();
|
||||
cmd_runscript();
|
||||
throw 1;
|
||||
}
|
||||
catch(int e)
|
||||
{
|
||||
if (e != 0)
|
||||
throw 1;
|
||||
if (e != 0) throw 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,108 +66,6 @@ void CgiHandler::cmd_cfg_jsonserver()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CgiHandler::cmd_cfg_get()
|
||||
{
|
||||
if ( _args.at(0) == "cfg_get" )
|
||||
{
|
||||
QFile file ( _hyperion->getConfigFileName() );
|
||||
if (file.exists ())
|
||||
{
|
||||
if (file.open (QFile::ReadOnly)) {
|
||||
QByteArray data = file.readAll ();
|
||||
_reply->addHeader ("Content-Type", "text/plain");
|
||||
_reply->appendRawData (data);
|
||||
file.close ();
|
||||
}
|
||||
}
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CgiHandler::cmd_cfg_set()
|
||||
{
|
||||
_reply->addHeader ("Content-Type", "text/plain");
|
||||
if ( _args.at(0) == "cfg_set" )
|
||||
{
|
||||
QtHttpPostData data = _request->getPostData();
|
||||
QJsonParseError error;
|
||||
if (data.contains("cfg"))
|
||||
{
|
||||
QJsonDocument hyperionConfig = QJsonDocument::fromJson(QByteArray::fromPercentEncoding(data["cfg"]), &error);
|
||||
|
||||
if (error.error == QJsonParseError::NoError)
|
||||
{
|
||||
QJsonObject hyperionConfigJsonObj = hyperionConfig.object();
|
||||
try
|
||||
{
|
||||
// make sure the resources are loaded (they may be left out after static linking)
|
||||
Q_INIT_RESOURCE(resource);
|
||||
|
||||
QString schemaFile = ":/hyperion-schema";
|
||||
QJsonObject schemaJson;
|
||||
|
||||
try
|
||||
{
|
||||
schemaJson = QJsonFactory::readSchema(schemaFile);
|
||||
}
|
||||
catch(const std::runtime_error& error)
|
||||
{
|
||||
throw std::runtime_error(error.what());
|
||||
}
|
||||
|
||||
QJsonSchemaChecker schemaChecker;
|
||||
schemaChecker.setSchema(schemaJson);
|
||||
|
||||
QPair<bool, bool> validate = schemaChecker.validate(hyperionConfigJsonObj);
|
||||
|
||||
|
||||
if (validate.first && validate.second)
|
||||
{
|
||||
QJsonFactory::writeJson(_hyperion->getConfigFileName(), hyperionConfigJsonObj);
|
||||
}
|
||||
else if (!validate.first && validate.second)
|
||||
{
|
||||
Warning(_log,"Errors have been found in the configuration file. Automatic correction is applied");
|
||||
|
||||
QStringList schemaErrors = schemaChecker.getMessages();
|
||||
foreach (auto & schemaError, schemaErrors)
|
||||
Info(_log, schemaError.toUtf8().constData());
|
||||
|
||||
hyperionConfigJsonObj = schemaChecker.getAutoCorrectedConfig(hyperionConfigJsonObj);
|
||||
|
||||
if (!QJsonFactory::writeJson(_hyperion->getConfigFileName(), hyperionConfigJsonObj))
|
||||
throw std::runtime_error("ERROR: can not save configuration file, aborting ");
|
||||
}
|
||||
else //Error in Schema
|
||||
{
|
||||
QString errorMsg = "ERROR: Json validation failed: \n";
|
||||
QStringList schemaErrors = schemaChecker.getMessages();
|
||||
foreach (auto & schemaError, schemaErrors)
|
||||
{
|
||||
Error(_log, "config write validation: %s", QSTRING_CSTR(schemaError));
|
||||
errorMsg += schemaError + "\n";
|
||||
}
|
||||
|
||||
throw std::runtime_error(errorMsg.toStdString());
|
||||
}
|
||||
}
|
||||
catch(const std::runtime_error& validate_error)
|
||||
{
|
||||
_reply->appendRawData (QString(validate_error.what()).toUtf8());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug(_log, "error while saving: %s", error.errorString()).toLocal8bit.constData());
|
||||
_reply->appendRawData (QString("Error while validating json: "+error.errorString()).toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CgiHandler::cmd_runscript()
|
||||
{
|
||||
if ( _args.at(0) == "run" )
|
||||
|
@ -22,8 +22,6 @@ public:
|
||||
|
||||
// cgi commands
|
||||
void cmd_cfg_jsonserver();
|
||||
void cmd_cfg_get ();
|
||||
void cmd_cfg_set ();
|
||||
void cmd_runscript ();
|
||||
|
||||
private:
|
||||
|
Loading…
Reference in New Issue
Block a user