mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
code refactoring
Former-commit-id: 157f424e83c58fca4ed1e3283899cbbdfadcffe1
This commit is contained in:
parent
6a9e75243d
commit
9168ee5098
@ -27,9 +27,9 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket, Hyperion * hyperi
|
|||||||
_socket(socket),
|
_socket(socket),
|
||||||
_imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()),
|
_imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()),
|
||||||
_hyperion(hyperion),
|
_hyperion(hyperion),
|
||||||
_receiveBuffer()
|
_receiveBuffer(),
|
||||||
|
_webSocketHandshakeDone(false)
|
||||||
{
|
{
|
||||||
_webSocketHandshakeDone = false;
|
|
||||||
// connect internal signals and slots
|
// connect internal signals and slots
|
||||||
connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
|
connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
|
||||||
connect(_socket, SIGNAL(readyRead()), this, SLOT(readData()));
|
connect(_socket, SIGNAL(readyRead()), this, SLOT(readData()));
|
||||||
@ -45,106 +45,19 @@ void JsonClientConnection::readData()
|
|||||||
{
|
{
|
||||||
_receiveBuffer += _socket->readAll();
|
_receiveBuffer += _socket->readAll();
|
||||||
|
|
||||||
if (_webSocketHandshakeDone) { // websocket mode, data frame
|
if (_webSocketHandshakeDone)
|
||||||
quint8 opCode = 0;
|
{
|
||||||
quint64 payloadLength = 0;
|
// websocket mode, data frame
|
||||||
bool isMasked = false;
|
handleWebSocketFrame();
|
||||||
quint32 index = 0;
|
} else
|
||||||
quint8 maskKey[4];
|
{
|
||||||
|
// might be a handshake request or raw socket data
|
||||||
if ((_receiveBuffer.at(0) & 0x80) == 0x80) { // final bit
|
if(_receiveBuffer.contains("Upgrade: websocket"))
|
||||||
opCode = _receiveBuffer.at(0) & 0x0F;
|
{
|
||||||
isMasked = (_receiveBuffer.at(1) & 0x80) == 0x80;
|
doWebSocketHandshake();
|
||||||
payloadLength = _receiveBuffer.at(1) & 0x7F;
|
} else
|
||||||
index = 2;
|
{
|
||||||
|
// raw socket data, handling as usual
|
||||||
switch (payloadLength) {
|
|
||||||
case 126:
|
|
||||||
payloadLength = ((_receiveBuffer.at(2) << 8) & 0xFF00) | (_receiveBuffer.at(3) & 0xFF);
|
|
||||||
index += 2;
|
|
||||||
break;
|
|
||||||
case 127: {
|
|
||||||
payloadLength = 0;
|
|
||||||
for (uint i=0; i < 8; i++) {
|
|
||||||
payloadLength |= ((quint64)(_receiveBuffer.at(index+i) & 0xFF)) << (8*(7-i));
|
|
||||||
}
|
|
||||||
index += 8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMasked) { // if the data is masked we need to get the key for unmasking
|
|
||||||
for (uint i=0; i < 4; i++) {
|
|
||||||
maskKey[i] = _receiveBuffer.at(index + i);
|
|
||||||
}
|
|
||||||
index += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the type of data frame
|
|
||||||
switch (opCode) {
|
|
||||||
case 0x01: { // text
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMessage(QString(result).toStdString());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x08: { // close
|
|
||||||
quint8 close[]={0x88, 0};
|
|
||||||
_socket->write((const char*)close, 2);
|
|
||||||
_socket->flush();
|
|
||||||
_socket->close();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x09: { // ping, send pong
|
|
||||||
quint8 close[]={0x0A, 0};
|
|
||||||
_socket->write((const char*)close, 2);
|
|
||||||
_socket->flush();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cout << "Someone is sending very big messages over several frames... it's not supported yet" << std::endl;
|
|
||||||
quint8 close[]={0x88, 0};
|
|
||||||
_socket->write((const char*)close, 2);
|
|
||||||
_socket->flush();
|
|
||||||
_socket->close();
|
|
||||||
}
|
|
||||||
} else { // might be a handshake request or raw socket data
|
|
||||||
if(_receiveBuffer.contains("Upgrade: websocket")){ // http header, might not be a very reliable check...
|
|
||||||
std::cout << "Websocket handshake" << std::endl;
|
|
||||||
|
|
||||||
// get the key to tprepare an answer
|
|
||||||
int start = _receiveBuffer.indexOf("Sec-WebSocket-Key") + 19;
|
|
||||||
std::string value(_receiveBuffer.mid(start, _receiveBuffer.indexOf("\r\n", start) - start).data());
|
|
||||||
_receiveBuffer.clear();
|
|
||||||
|
|
||||||
// must be always appended
|
|
||||||
value += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
||||||
|
|
||||||
// generate sha1 hash
|
|
||||||
QByteArray hash = QCryptographicHash::hash(value.c_str(), QCryptographicHash::Sha1);
|
|
||||||
|
|
||||||
// prepare an answer
|
|
||||||
std::ostringstream h;
|
|
||||||
h << "HTTP/1.1 101 Switching Protocols\r\n" <<
|
|
||||||
"Upgrade: websocket\r\n" <<
|
|
||||||
"Connection: Upgrade\r\n" <<
|
|
||||||
"Sec-WebSocket-Accept: " << QString(hash.toBase64()).toStdString() << "\r\n\r\n";
|
|
||||||
|
|
||||||
_socket->write(h.str().c_str());
|
|
||||||
_socket->flush();
|
|
||||||
_webSocketHandshakeDone = true; // we are in WebSocket mode, data frames should follow next
|
|
||||||
} else { // raw socket data, handling as usual
|
|
||||||
int bytes = _receiveBuffer.indexOf('\n') + 1;
|
int bytes = _receiveBuffer.indexOf('\n') + 1;
|
||||||
while(bytes > 0)
|
while(bytes > 0)
|
||||||
{
|
{
|
||||||
@ -164,6 +77,128 @@ void JsonClientConnection::readData()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonClientConnection::handleWebSocketFrame()
|
||||||
|
{
|
||||||
|
if ((_receiveBuffer.at(0) & 0x80) == 0x80)
|
||||||
|
{
|
||||||
|
// final bit found, frame complete
|
||||||
|
quint8 * maskKey = NULL;
|
||||||
|
quint8 opCode = _receiveBuffer.at(0) & 0x0F;
|
||||||
|
bool isMasked = (_receiveBuffer.at(1) & 0x80) == 0x80;
|
||||||
|
quint64 payloadLength = _receiveBuffer.at(1) & 0x7F;
|
||||||
|
quint32 index = 2;
|
||||||
|
|
||||||
|
switch (payloadLength)
|
||||||
|
{
|
||||||
|
case 126:
|
||||||
|
payloadLength = ((_receiveBuffer.at(2) << 8) & 0xFF00) | (_receiveBuffer.at(3) & 0xFF);
|
||||||
|
index += 2;
|
||||||
|
break;
|
||||||
|
case 127:
|
||||||
|
payloadLength = 0;
|
||||||
|
for (uint i=0; i < 8; i++) {
|
||||||
|
payloadLength |= ((quint64)(_receiveBuffer.at(index+i) & 0xFF)) << (8*(7-i));
|
||||||
|
}
|
||||||
|
index += 8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMasked)
|
||||||
|
{
|
||||||
|
// if the data is masked we need to get the key for unmasking
|
||||||
|
maskKey = new quint8[4];
|
||||||
|
for (uint i=0; i < 4; i++)
|
||||||
|
{
|
||||||
|
maskKey[i] = _receiveBuffer.at(index + i);
|
||||||
|
}
|
||||||
|
index += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the type of data frame
|
||||||
|
switch (opCode)
|
||||||
|
{
|
||||||
|
case 0x01:
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMessage(QString(result).toStdString());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
{
|
||||||
|
// close request, confirm
|
||||||
|
quint8 close[] = {0x88, 0};
|
||||||
|
_socket->write((const char*)close, 2);
|
||||||
|
_socket->flush();
|
||||||
|
_socket->close();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x09:
|
||||||
|
{
|
||||||
|
// ping received, send pong
|
||||||
|
quint8 pong[] = {0x0A, 0};
|
||||||
|
_socket->write((const char*)pong, 2);
|
||||||
|
_socket->flush();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
std::cout << "Someone is sending very big messages over several frames... it's not supported yet" << std::endl;
|
||||||
|
quint8 close[] = {0x88, 0};
|
||||||
|
_socket->write((const char*)close, 2);
|
||||||
|
_socket->flush();
|
||||||
|
_socket->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonClientConnection::doWebSocketHandshake()
|
||||||
|
{
|
||||||
|
// http header, might not be a very reliable check...
|
||||||
|
std::cout << "Websocket handshake" << std::endl;
|
||||||
|
|
||||||
|
// get the key to prepare an answer
|
||||||
|
int start = _receiveBuffer.indexOf("Sec-WebSocket-Key") + 19;
|
||||||
|
std::string value(_receiveBuffer.mid(start, _receiveBuffer.indexOf("\r\n", start) - start).data());
|
||||||
|
_receiveBuffer.clear();
|
||||||
|
|
||||||
|
// must be always appended
|
||||||
|
value += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||||
|
|
||||||
|
// generate sha1 hash
|
||||||
|
QByteArray hash = QCryptographicHash::hash(value.c_str(), QCryptographicHash::Sha1);
|
||||||
|
|
||||||
|
// prepare an answer
|
||||||
|
std::ostringstream h;
|
||||||
|
h << "HTTP/1.1 101 Switching Protocols\r\n" <<
|
||||||
|
"Upgrade: websocket\r\n" <<
|
||||||
|
"Connection: Upgrade\r\n" <<
|
||||||
|
"Sec-WebSocket-Accept: " << QString(hash.toBase64()).toStdString() << "\r\n\r\n";
|
||||||
|
|
||||||
|
_socket->write(h.str().c_str());
|
||||||
|
_socket->flush();
|
||||||
|
// we are in WebSocket mode, data frames should follow next
|
||||||
|
_webSocketHandshakeDone = true;
|
||||||
|
}
|
||||||
|
|
||||||
void JsonClientConnection::socketClosed()
|
void JsonClientConnection::socketClosed()
|
||||||
{
|
{
|
||||||
_webSocketHandshakeDone = false;
|
_webSocketHandshakeDone = false;
|
||||||
@ -472,15 +507,20 @@ void JsonClientConnection::sendMessage(const Json::Value &message)
|
|||||||
Json::FastWriter writer;
|
Json::FastWriter writer;
|
||||||
std::string serializedReply = writer.write(message);
|
std::string serializedReply = writer.write(message);
|
||||||
|
|
||||||
if (!_webSocketHandshakeDone) { // raw tcp socket mode
|
if (!_webSocketHandshakeDone)
|
||||||
|
{
|
||||||
|
// raw tcp socket mode
|
||||||
_socket->write(serializedReply.data(), serializedReply.length());
|
_socket->write(serializedReply.data(), serializedReply.length());
|
||||||
} else { // websocket mode
|
} else
|
||||||
|
{
|
||||||
|
// websocket mode
|
||||||
quint32 size = serializedReply.length();
|
quint32 size = serializedReply.length();
|
||||||
|
|
||||||
// prepare data frame
|
// prepare data frame
|
||||||
QByteArray response;
|
QByteArray response;
|
||||||
response.append(0x81);
|
response.append(0x81);
|
||||||
if (size > 125) {
|
if (size > 125)
|
||||||
|
{
|
||||||
response.append(0x7E);
|
response.append(0x7E);
|
||||||
response.append((size >> 8) & 0xFF);
|
response.append((size >> 8) & 0xFF);
|
||||||
response.append(size & 0xFF);
|
response.append(size & 0xFF);
|
||||||
@ -488,8 +528,7 @@ void JsonClientConnection::sendMessage(const Json::Value &message)
|
|||||||
response.append(size);
|
response.append(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray data(serializedReply.c_str(), serializedReply.length());
|
response.append(serializedReply.c_str(), serializedReply.length());
|
||||||
response.append(data);
|
|
||||||
|
|
||||||
_socket->write(response.data(), response.length());
|
_socket->write(response.data(), response.length());
|
||||||
}
|
}
|
||||||
|
@ -136,6 +136,16 @@ private:
|
|||||||
/// @param error String describing the error
|
/// @param error String describing the error
|
||||||
///
|
///
|
||||||
void sendErrorReply(const std::string & error);
|
void sendErrorReply(const std::string & error);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Do handshake for a websocket connection
|
||||||
|
///
|
||||||
|
void doWebSocketHandshake();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Handle incoming websocket data frame
|
||||||
|
///
|
||||||
|
void handleWebSocketFrame();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
///
|
///
|
||||||
|
Loading…
Reference in New Issue
Block a user