10
10
// Qt includes
11
11
#include < QResource>
12
12
#include < QDateTime>
13
+ #include < QCryptographicHash>
14
+ #include < QHostInfo>
13
15
14
16
// hyperion util includes
15
17
#include < hyperion/ImageProcessorFactory.h>
@@ -25,7 +27,8 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket, Hyperion * hyperi
25
27
_socket(socket),
26
28
_imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()),
27
29
_hyperion(hyperion),
28
- _receiveBuffer()
30
+ _receiveBuffer(),
31
+ _webSocketHandshakeDone(false )
29
32
{
30
33
// connect internal signals and slots
31
34
connect (_socket, SIGNAL (disconnected ()), this , SLOT (socketClosed ()));
@@ -41,26 +44,164 @@ JsonClientConnection::~JsonClientConnection()
41
44
void JsonClientConnection::readData ()
42
45
{
43
46
_receiveBuffer += _socket->readAll ();
44
-
45
- int bytes = _receiveBuffer.indexOf (' \n ' ) + 1 ;
46
- while (bytes > 0 )
47
+
48
+ if (_webSocketHandshakeDone)
47
49
{
48
- // create message string
49
- std::string message (_receiveBuffer.data (), bytes);
50
-
51
- // remove message data from buffer
52
- _receiveBuffer = _receiveBuffer.mid (bytes);
53
-
54
- // handle message
55
- handleMessage (message);
50
+ // websocket mode, data frame
51
+ handleWebSocketFrame ();
52
+ } else
53
+ {
54
+ // might be a handshake request or raw socket data
55
+ if (_receiveBuffer.contains (" Upgrade: websocket" ))
56
+ {
57
+ doWebSocketHandshake ();
58
+ } else
59
+ {
60
+ // raw socket data, handling as usual
61
+ int bytes = _receiveBuffer.indexOf (' \n ' ) + 1 ;
62
+ while (bytes > 0 )
63
+ {
64
+ // create message string
65
+ std::string message (_receiveBuffer.data (), bytes);
66
+
67
+ // remove message data from buffer
68
+ _receiveBuffer = _receiveBuffer.mid (bytes);
69
+
70
+ // handle message
71
+ handleMessage (message);
72
+
73
+ // try too look up '\n' again
74
+ bytes = _receiveBuffer.indexOf (' \n ' ) + 1 ;
75
+ }
76
+ }
77
+ }
78
+ }
56
79
57
- // try too look up '\n' again
58
- bytes = _receiveBuffer.indexOf (' \n ' ) + 1 ;
80
+ void JsonClientConnection::handleWebSocketFrame ()
81
+ {
82
+ if ((_receiveBuffer.at (0 ) & 0x80 ) == 0x80 )
83
+ {
84
+ // final bit found, frame complete
85
+ quint8 * maskKey = NULL ;
86
+ quint8 opCode = _receiveBuffer.at (0 ) & 0x0F ;
87
+ bool isMasked = (_receiveBuffer.at (1 ) & 0x80 ) == 0x80 ;
88
+ quint64 payloadLength = _receiveBuffer.at (1 ) & 0x7F ;
89
+ quint32 index = 2 ;
90
+
91
+ switch (payloadLength)
92
+ {
93
+ case 126 :
94
+ payloadLength = ((_receiveBuffer.at (2 ) << 8 ) & 0xFF00 ) | (_receiveBuffer.at (3 ) & 0xFF );
95
+ index += 2 ;
96
+ break ;
97
+ case 127 :
98
+ payloadLength = 0 ;
99
+ for (uint i=0 ; i < 8 ; i++) {
100
+ payloadLength |= ((quint64)(_receiveBuffer.at (index+i) & 0xFF )) << (8 *(7 -i));
101
+ }
102
+ index += 8 ;
103
+ break ;
104
+ default :
105
+ break ;
106
+ }
107
+
108
+ if (isMasked)
109
+ {
110
+ // if the data is masked we need to get the key for unmasking
111
+ maskKey = new quint8[4 ];
112
+ for (uint i=0 ; i < 4 ; i++)
113
+ {
114
+ maskKey[i] = _receiveBuffer.at (index + i);
115
+ }
116
+ index += 4 ;
117
+ }
118
+
119
+ // check the type of data frame
120
+ switch (opCode)
121
+ {
122
+ case 0x01 :
123
+ {
124
+ // frame contains text, extract it
125
+ QByteArray result = _receiveBuffer.mid (index, payloadLength);
126
+ _receiveBuffer.clear ();
127
+
128
+ // unmask data if necessary
129
+ if (isMasked)
130
+ {
131
+ for (uint i=0 ; i < payloadLength; i++)
132
+ {
133
+ result[i] = (result[i] ^ maskKey[i % 4 ]);
134
+ }
135
+ if (maskKey != NULL )
136
+ {
137
+ delete[] maskKey;
138
+ maskKey = NULL ;
139
+ }
140
+ }
141
+
142
+ handleMessage (QString (result).toStdString ());
143
+ }
144
+ break ;
145
+ case 0x08 :
146
+ {
147
+ // close request, confirm
148
+ quint8 close[] = {0x88 , 0 };
149
+ _socket->write ((const char *)close, 2 );
150
+ _socket->flush ();
151
+ _socket->close ();
152
+ }
153
+ break ;
154
+ case 0x09 :
155
+ {
156
+ // ping received, send pong
157
+ quint8 pong[] = {0x0A , 0 };
158
+ _socket->write ((const char *)pong, 2 );
159
+ _socket->flush ();
160
+ }
161
+ break ;
162
+ }
163
+ } else
164
+ {
165
+ std::cout << " Someone is sending very big messages over several frames... it's not supported yet" << std::endl;
166
+ quint8 close[] = {0x88 , 0 };
167
+ _socket->write ((const char *)close, 2 );
168
+ _socket->flush ();
169
+ _socket->close ();
59
170
}
60
171
}
61
172
173
+ void JsonClientConnection::doWebSocketHandshake ()
174
+ {
175
+ // http header, might not be a very reliable check...
176
+ std::cout << " Websocket handshake" << std::endl;
177
+
178
+ // get the key to prepare an answer
179
+ int start = _receiveBuffer.indexOf (" Sec-WebSocket-Key" ) + 19 ;
180
+ std::string value (_receiveBuffer.mid (start, _receiveBuffer.indexOf (" \r\n " , start) - start).data ());
181
+ _receiveBuffer.clear ();
182
+
183
+ // must be always appended
184
+ value += " 258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ;
185
+
186
+ // generate sha1 hash
187
+ QByteArray hash = QCryptographicHash::hash (value.c_str (), QCryptographicHash::Sha1);
188
+
189
+ // prepare an answer
190
+ std::ostringstream h;
191
+ h << " HTTP/1.1 101 Switching Protocols\r\n " <<
192
+ " Upgrade: websocket\r\n " <<
193
+ " Connection: Upgrade\r\n " <<
194
+ " Sec-WebSocket-Accept: " << QString (hash.toBase64 ()).toStdString () << " \r\n\r\n " ;
195
+
196
+ _socket->write (h.str ().c_str ());
197
+ _socket->flush ();
198
+ // we are in WebSocket mode, data frames should follow next
199
+ _webSocketHandshakeDone = true ;
200
+ }
201
+
62
202
void JsonClientConnection::socketClosed ()
63
203
{
204
+ _webSocketHandshakeDone = false ;
64
205
emit connectionClosed (this );
65
206
}
66
207
@@ -205,6 +346,9 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &)
205
346
Json::Value result;
206
347
result[" success" ] = true ;
207
348
Json::Value & info = result[" info" ];
349
+
350
+ // add host name for remote clients
351
+ info[" hostname" ] = QHostInfo::localHostName ().toStdString ();
208
352
209
353
// collect priority information
210
354
Json::Value & priorities = info[" priorities" ] = Json::Value (Json::arrayValue);
@@ -362,7 +506,32 @@ void JsonClientConnection::sendMessage(const Json::Value &message)
362
506
{
363
507
Json::FastWriter writer;
364
508
std::string serializedReply = writer.write (message);
365
- _socket->write (serializedReply.data (), serializedReply.length ());
509
+
510
+ if (!_webSocketHandshakeDone)
511
+ {
512
+ // raw tcp socket mode
513
+ _socket->write (serializedReply.data (), serializedReply.length ());
514
+ } else
515
+ {
516
+ // websocket mode
517
+ quint32 size = serializedReply.length ();
518
+
519
+ // prepare data frame
520
+ QByteArray response;
521
+ response.append (0x81 );
522
+ if (size > 125 )
523
+ {
524
+ response.append (0x7E );
525
+ response.append ((size >> 8 ) & 0xFF );
526
+ response.append (size & 0xFF );
527
+ } else {
528
+ response.append (size);
529
+ }
530
+
531
+ response.append (serializedReply.c_str (), serializedReply.length ());
532
+
533
+ _socket->write (response.data (), response.length ());
534
+ }
366
535
}
367
536
368
537
void JsonClientConnection::sendSuccessReply ()
0 commit comments