diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index d50ed951..87fb7c1f 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -27,9 +27,9 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket, Hyperion * hyperi _socket(socket), _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()), _hyperion(hyperion), - _receiveBuffer() + _receiveBuffer(), + _webSocketHandshakeDone(false) { - _webSocketHandshakeDone = false; // connect internal signals and slots connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed())); connect(_socket, SIGNAL(readyRead()), this, SLOT(readData())); @@ -45,106 +45,19 @@ void JsonClientConnection::readData() { _receiveBuffer += _socket->readAll(); - if (_webSocketHandshakeDone) { // websocket mode, data frame - quint8 opCode = 0; - quint64 payloadLength = 0; - bool isMasked = false; - quint32 index = 0; - quint8 maskKey[4]; - - if ((_receiveBuffer.at(0) & 0x80) == 0x80) { // final bit - opCode = _receiveBuffer.at(0) & 0x0F; - isMasked = (_receiveBuffer.at(1) & 0x80) == 0x80; - payloadLength = _receiveBuffer.at(1) & 0x7F; - 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 - 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 + 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) { @@ -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() { _webSocketHandshakeDone = false; @@ -472,15 +507,20 @@ void JsonClientConnection::sendMessage(const Json::Value &message) Json::FastWriter writer; std::string serializedReply = writer.write(message); - if (!_webSocketHandshakeDone) { // raw tcp socket mode + if (!_webSocketHandshakeDone) + { + // raw tcp socket mode _socket->write(serializedReply.data(), serializedReply.length()); - } else { // websocket mode + } else + { + // websocket mode quint32 size = serializedReply.length(); // prepare data frame QByteArray response; response.append(0x81); - if (size > 125) { + if (size > 125) + { response.append(0x7E); response.append((size >> 8) & 0xFF); response.append(size & 0xFF); @@ -488,8 +528,7 @@ void JsonClientConnection::sendMessage(const Json::Value &message) response.append(size); } - QByteArray data(serializedReply.c_str(), serializedReply.length()); - response.append(data); + response.append(serializedReply.c_str(), serializedReply.length()); _socket->write(response.data(), response.length()); } diff --git a/libsrc/jsonserver/JsonClientConnection.h b/libsrc/jsonserver/JsonClientConnection.h index b036e638..6575388a 100644 --- a/libsrc/jsonserver/JsonClientConnection.h +++ b/libsrc/jsonserver/JsonClientConnection.h @@ -136,6 +136,16 @@ private: /// @param error String describing the error /// void sendErrorReply(const std::string & error); + + /// + /// Do handshake for a websocket connection + /// + void doWebSocketHandshake(); + + /// + /// Handle incoming websocket data frame + /// + void handleWebSocketFrame(); private: ///