Skip to content
This repository was archived by the owner on May 6, 2021. It is now read-only.

Commit 5d62331

Browse files
committed
Added very basic WebSocket support to json server
1 parent 25438c1 commit 5d62331

File tree

2 files changed

+153
-2
lines changed

2 files changed

+153
-2
lines changed

libsrc/jsonserver/JsonClientConnection.cpp

Lines changed: 150 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
// Qt includes
1111
#include <QResource>
1212
#include <QDateTime>
13+
#include <QCryptographicHash>
14+
#include <QHostInfo>
1315

1416
// hyperion util includes
1517
#include <hyperion/ImageProcessorFactory.h>
@@ -27,6 +29,7 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket, Hyperion * hyperi
2729
_hyperion(hyperion),
2830
_receiveBuffer()
2931
{
32+
_webSocketHandshakeDone = false;
3033
// connect internal signals and slots
3134
connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
3235
connect(_socket, SIGNAL(readyRead()), this, SLOT(readData()));
@@ -41,7 +44,126 @@ JsonClientConnection::~JsonClientConnection()
4144
void JsonClientConnection::readData()
4245
{
4346
_receiveBuffer += _socket->readAll();
44-
47+
48+
if (_webSocketHandshakeDone) { // websocket mode, data frame
49+
quint8 opCode = 0;
50+
quint64 payloadLength = 0;
51+
bool isMasked = false;
52+
quint32 index = 0;
53+
quint8 maskKey[4];
54+
55+
if ((_receiveBuffer.at(0) & 0x80) == 0x80) { // final bit
56+
opCode = _receiveBuffer.at(0) & 0x0F;
57+
isMasked = (_receiveBuffer.at(1) & 0x80) == 0x80;
58+
payloadLength = _receiveBuffer.at(1) & 0x7F;
59+
index = 2;
60+
61+
switch (payloadLength) {
62+
case 126:
63+
payloadLength = ((_receiveBuffer.at(2) << 8) & 0xFF00) | (_receiveBuffer.at(3) & 0xFF);
64+
index += 2;
65+
break;
66+
case 127: {
67+
payloadLength = 0;
68+
for (int i=0; i < 8; i++) {
69+
payloadLength |= ((quint64)(_receiveBuffer.at(index+i) & 0xFF)) << (8*(7-i));
70+
}
71+
index += 8;
72+
}
73+
break;
74+
default:
75+
break;
76+
}
77+
78+
if (isMasked) { // if the data is masked we need to get the key for unmasking
79+
for (int i=0; i < 4; i++) {
80+
maskKey[i] = _receiveBuffer.at(index + i);
81+
}
82+
index += 4;
83+
}
84+
85+
// check the type of data frame
86+
switch (opCode) {
87+
case 0x01: { // text
88+
QByteArray result = _receiveBuffer.mid(index, payloadLength);
89+
_receiveBuffer.clear();
90+
91+
// unmask data if necessary
92+
if (isMasked) {
93+
for (uint i=0 ; i < payloadLength; i++) {
94+
result[i] = (result[i] ^ maskKey[i % 4]);
95+
}
96+
}
97+
98+
handleMessage(QString(result).toStdString());
99+
}
100+
break;
101+
case 0x08: { // close
102+
quint8 close[]={0x88, 0};
103+
_socket->write((const char*)close, 2);
104+
_socket->flush();
105+
_socket->close();
106+
}
107+
break;
108+
case 0x09: { // ping, send pong
109+
quint8 close[]={0x0A, 0};
110+
_socket->write((const char*)close, 2);
111+
_socket->flush();
112+
}
113+
break;
114+
}
115+
} else {
116+
std::cout << "Someone is sending very big messages over several frames... it's not supported yet" << std::endl;
117+
quint8 close[]={0x88, 0};
118+
_socket->write((const char*)close, 2);
119+
_socket->flush();
120+
_socket->close();
121+
}
122+
} else { // might be a handshake request or raw socket data
123+
if(_receiveBuffer.contains("Upgrade: websocket")){ // http header, might not be a very reliable check...
124+
std::cout << "Websocket handshake" << std::endl;
125+
126+
// get the key to tprepare an answer
127+
int start = _receiveBuffer.indexOf("Sec-WebSocket-Key") + 19;
128+
std::string value(_receiveBuffer.mid(start, _receiveBuffer.indexOf("\r\n", start) - start).data());
129+
_receiveBuffer.clear();
130+
131+
// must be always appended
132+
value += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
133+
134+
// generate sha1 hash
135+
QByteArray hash = QCryptographicHash::hash(value.c_str(), QCryptographicHash::Sha1);
136+
137+
// prepare an answer
138+
std::ostringstream h;
139+
h << "HTTP/1.1 101 Switching Protocols\r\n" <<
140+
"Upgrade: websocket\r\n" <<
141+
"Connection: Upgrade\r\n" <<
142+
"Sec-WebSocket-Accept: " << QString(hash.toBase64()).toStdString() << "\r\n\r\n";
143+
144+
_socket->write(h.str().c_str());
145+
_socket->flush();
146+
_webSocketHandshakeDone = true; // we are in WebSocket mode, data frames should follow next
147+
} else { // raw socket data, handling as usual
148+
int bytes = _receiveBuffer.indexOf('\n') + 1;
149+
while(bytes > 0)
150+
{
151+
// create message string
152+
std::string message(_receiveBuffer.data(), bytes);
153+
154+
// remove message data from buffer
155+
_receiveBuffer = _receiveBuffer.mid(bytes);
156+
157+
// handle message
158+
handleMessage(message);
159+
160+
// try too look up '\n' again
161+
bytes = _receiveBuffer.indexOf('\n') + 1;
162+
}
163+
}
164+
}
165+
166+
/*
45167
int bytes = _receiveBuffer.indexOf('\n') + 1;
46168
while(bytes > 0)
47169
{
@@ -57,10 +179,12 @@ void JsonClientConnection::readData()
57179
// try too look up '\n' again
58180
bytes = _receiveBuffer.indexOf('\n') + 1;
59181
}
182+
*/
60183
}
61184

62185
void JsonClientConnection::socketClosed()
63186
{
187+
_webSocketHandshakeDone = false;
64188
emit connectionClosed(this);
65189
}
66190

@@ -205,6 +329,9 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &)
205329
Json::Value result;
206330
result["success"] = true;
207331
Json::Value & info = result["info"];
332+
333+
// add host name for remote clients
334+
info["hostname"] = QHostInfo::localHostName().toStdString();
208335

209336
// collect priority information
210337
Json::Value & priorities = info["priorities"] = Json::Value(Json::arrayValue);
@@ -362,7 +489,28 @@ void JsonClientConnection::sendMessage(const Json::Value &message)
362489
{
363490
Json::FastWriter writer;
364491
std::string serializedReply = writer.write(message);
365-
_socket->write(serializedReply.data(), serializedReply.length());
492+
493+
if (!_webSocketHandshakeDone) { // raw tcp socket mode
494+
_socket->write(serializedReply.data(), serializedReply.length());
495+
} else { // websocket mode
496+
quint32 size = serializedReply.length();
497+
498+
// prepare data frame
499+
QByteArray response;
500+
response.append(0x81);
501+
if (size > 125) {
502+
response.append(0x7E);
503+
response.append((size >> 8) & 0xFF);
504+
response.append(size & 0xFF);
505+
} else {
506+
response.append(size);
507+
}
508+
509+
QByteArray data(serializedReply.c_str(), serializedReply.length());
510+
response.append(data);
511+
512+
_socket->write(response.data(), response.length());
513+
}
366514
}
367515

368516
void JsonClientConnection::sendSuccessReply()

libsrc/jsonserver/JsonClientConnection.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,7 @@ private slots:
161161

162162
/// The buffer used for reading data from the socket
163163
QByteArray _receiveBuffer;
164+
165+
/// used for WebSocket detection and connection handling
166+
bool _webSocketHandshakeDone;
164167
};

0 commit comments

Comments
 (0)