14 #include <QCryptographicHash>
26 m_threadPool(
"WebSocketServerPool")
28 setObjectName(
"WebSocketServer");
30 int maxThreads = qMax(QThread::idealThreadCount() * 2, 2);
36 LOG(VB_HTTP, LOG_NOTICE, QString(
"WebSocketServer(): Max Thread Count %1")
53 auto *server = qobject_cast<PrivTcpServer *>(QObject::sender());
55 type = server->GetServerType();
63 QString(
"WebSocketServer%1").
arg(socket));
73 ,
const QSslConfiguration& sslConfig
77 m_webSocketServer(webSocketServer), m_socketFD(sock),
78 m_connectionType(
type)
80 , m_sslConfig(sslConfig)
95 worker->deleteLater();
104 #ifndef QT_NO_OPENSSL
105 ,
const QSslConfiguration& sslConfig
108 : m_eventLoop(new QEventLoop()),
109 m_webSocketServer(webSocketServer),
110 m_socketFD(sock), m_connectionType(
type),
111 m_heartBeat(new QTimer())
112 #ifndef QT_NO_OPENSSL
113 , m_sslConfig(sslConfig)
116 setObjectName(QString(
"WebSocketWorker(%1)")
118 LOG(VB_HTTP, LOG_INFO, QString(
"WebSocketWorker(%1): New connection")
138 extension->deleteLater();
156 LOG(VB_HTTP, LOG_INFO,
"WebSocketWorker::CloseConnection()");
166 #ifndef QT_NO_OPENSSL
167 auto *pSslSocket =
new QSslSocket();
168 if (pSslSocket->setSocketDescriptor(
m_socketFD)
172 pSslSocket->startServerEncryption();
173 if (pSslSocket->waitForEncrypted(5000))
175 LOG(VB_HTTP, LOG_INFO,
"SSL Handshake occurred, connection encrypted");
176 LOG(VB_HTTP, LOG_INFO, QString(
"Using %1 cipher").
arg(pSslSocket->sessionCipher().name()));
180 LOG(VB_HTTP, LOG_WARNING,
"SSL Handshake FAILED, connection terminated");
182 pSslSocket =
nullptr;
188 pSslSocket =
nullptr;
212 m_socket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
225 if (
m_socket->error() != QAbstractSocket::UnknownSocketError)
227 LOG(VB_HTTP, LOG_WARNING, QString(
"WebSocketWorker(%1): Error %2 (%3)")
233 std::chrono::milliseconds writeTimeout = 5s;
237 m_socket->state() == QAbstractSocket::ConnectedState &&
240 LOG(VB_HTTP, LOG_DEBUG, QString(
"WebSocketWorker(%1): "
241 "Waiting for %2 bytes to be written "
242 "before closing the connection.")
254 if (!
m_socket->waitForBytesWritten(writeTimeout.count()))
256 LOG(VB_GENERAL, LOG_WARNING, QString(
"WebSocketWorker(%1): "
257 "Timed out waiting to write bytes to "
258 "the socket, waited %2 seconds")
260 .
arg(writeTimeout.count() / 1000));
267 LOG(VB_HTTP, LOG_WARNING, QString(
"WebSocketWorker(%1): "
268 "Failed to write %2 bytes to "
275 LOG(VB_HTTP, LOG_INFO, QString(
"WebSocketWorker(%1): Connection %2 closed.")
307 LOG(VB_HTTP, LOG_WARNING,
"WebSocketServer: Timed out waiting for connection upgrade");
318 while (socket->bytesAvailable() < 16)
320 if (!socket->waitForReadyRead(5000))
326 line = socket->readLine(255).trimmed();
330 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
331 QStringList tokens = QString(line).split(
' ', QString::SkipEmptyParts);
333 QStringList tokens = QString(line).split(
' ', Qt::SkipEmptyParts);
336 if (tokens.length() != 3)
338 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Invalid number of tokens in Request line");
342 if (tokens[0] !=
"GET")
344 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Invalid method: %1").
arg(tokens[0]));
348 QString path = tokens[1];
350 if (path.contains(
'#'))
352 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Path contains illegal fragment");
356 if (!tokens[2].startsWith(
"HTTP/"))
358 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Invalid protocol: %1").
arg(tokens[2]));
362 QString versionStr = tokens[2].section(
'/', 1, 1);
364 int majorVersion = versionStr.section(
'.', 0, 0).toInt();
365 int minorVersion = versionStr.section(
'.', 1, 1).toInt();
367 if ((majorVersion < 1) || (minorVersion < 1))
369 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Invalid HTTP version: %1.%2").
arg(majorVersion).
arg(minorVersion));
374 line = socket->readLine(2000).trimmed();
376 QMap<QString, QString> requestHeaders;
377 while (!line.isEmpty())
379 QString strLine = QString(line);
380 QString sName = strLine.section(
':', 0, 0 ).trimmed();
381 QString sValue = strLine.section(
':', 1 ).trimmed();
382 sValue.replace(
" ",
"");
384 if (!sName.isEmpty() && !sValue.isEmpty())
385 requestHeaders.insert(sName.toLower(), sValue);
387 line = socket->readLine(2000).trimmed();
391 QMap<QString, QString>::iterator it;
392 for ( it = requestHeaders.begin();
393 it != requestHeaders.end();
396 LOG(VB_HTTP, LOG_INFO, QString(
"(Request Header) %1: %2")
397 .
arg(it.key()).arg(*it));
400 if (!requestHeaders.contains(
"connection"))
402 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing 'Connection' header");
406 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
407 QStringList connectionValues = requestHeaders[
"connection"].split(
',', QString::SkipEmptyParts);
409 QStringList connectionValues = requestHeaders[
"connection"].split(
',', Qt::SkipEmptyParts);
411 if (!connectionValues.contains(
"Upgrade", Qt::CaseInsensitive))
413 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Invalid 'Connection' header");
417 if (!requestHeaders.contains(
"upgrade") ||
418 requestHeaders[
"upgrade"].toLower() !=
"websocket")
420 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing or invalid 'Upgrade' header");
424 if (!requestHeaders.contains(
"sec-websocket-version"))
426 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing 'Sec-WebSocket-Version' header");
430 if (requestHeaders[
"sec-websocket-version"] !=
"13")
432 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Unsupported WebSocket protocol "
433 "version. We speak '13' the client speaks '%1'")
434 .
arg(requestHeaders[
"sec-websocket-version"]));
438 if (!requestHeaders.contains(
"sec-websocket-key") ||
439 QByteArray::fromBase64(requestHeaders[
"sec-websocket-key"].toLatin1()).length() != 16)
441 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing or invalid 'sec-websocket-key' header");
447 if (requestHeaders.contains(
"user-agent") &&
448 requestHeaders[
"user-agent"].contains(
"AutobahnTestSuite"))
451 QString key = requestHeaders[
"sec-websocket-key"];
452 const QString magicString =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
455 QString acceptToken = QCryptographicHash::hash(key.append(magicString).toUtf8(),
456 QCryptographicHash::Sha1).toBase64();
458 QMap<QString, QString> responseHeaders;
459 responseHeaders.insert(
"Connection",
"Upgrade");
460 responseHeaders.insert(
"Upgrade",
"websocket");
461 responseHeaders.insert(
"Sec-WebSocket-Accept", acceptToken);
463 socket->write(
"HTTP/1.1 101 Switching Protocols\r\n");
465 QString header(
"%1: %2\r\n");
466 for (it = responseHeaders.begin(); it != responseHeaders.end(); ++it)
468 socket->write(header.arg(it.key()).arg(*it).toLatin1().constData());
469 LOG(VB_HTTP, LOG_INFO, QString(
"(Response Header) %1: %2")
470 .
arg(it.key()).arg(*it));
473 socket->write(
"\r\n");
474 socket->waitForBytesWritten();
486 while (
m_isRunning && socket && socket->bytesAvailable() >= 2)
488 uint8_t headerSize = 2;
490 QByteArray header = socket->peek(headerSize);
496 if (header.at(0) & 0x70)
498 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() "
499 "- Invalid data in reserved fields");
504 uint8_t opCode = (header.at(0) & 0xF);
509 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessFrames() "
510 "- Invalid OpCode (%1)")
511 .
arg(QString::number(opCode, 16)));
516 frame.
m_isMasked = (((header.at(1) >> 7) & 0xFE) != 0);
525 uint8_t payloadHeaderSize = 2;
527 payloadHeaderSize = 8;
529 headerSize += payloadHeaderSize;
531 if (socket->bytesAvailable() < headerSize)
534 header = socket->peek(headerSize);
535 QByteArray payloadHeader = header.mid(2,payloadHeaderSize);
537 for (
int i = 0; i < payloadHeaderSize; i++)
539 frame.
m_payloadSize |= ((uint8_t)payloadHeader.at(i) << ((payloadHeaderSize - (i + 1)) * 8));
544 if (socket->bytesAvailable() < headerSize)
546 header = socket->peek(headerSize);
549 while ((uint64_t)socket->bytesAvailable() < (frame.
m_payloadSize + header.length()))
551 if (!socket->waitForReadyRead(2000))
558 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() - Timed out waiting for rest of frame to arrive.");
568 LOG(VB_HTTP, LOG_DEBUG, QString(
"Read Header: %1").
arg(QString(header.toHex())));
569 LOG(VB_HTTP, LOG_DEBUG, QString(
"Final Frame: %1").
arg(frame.
m_finalFrame ?
"Yes" :
"No"));
570 LOG(VB_HTTP, LOG_DEBUG, QString(
"Op Code: %1").
arg(QString::number(frame.
m_opCode)));
571 LOG(VB_HTTP, LOG_DEBUG, QString(
"Payload Size: %1 Bytes").
arg(QString::number(frame.
m_payloadSize)));
577 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() - Frame payload larger than limit of 1MB");
585 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() - Total payload larger than limit of 4MB");
590 socket->read(headerSize);
597 frame.
m_mask = header.right(4);
606 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Incomplete multi-part frame? Expected continuation.");
631 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Received Continuation Frame out of sequence");
646 [[clang::fallthrough]];
663 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Received Unknown Frame Type");
684 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Message is not valid UTF-8");
693 if (extension->HandleTextFrame(frame))
702 if (extension->HandleBinaryFrame(frame))
727 if (payload.length() == 1)
729 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Invalid close payload");
734 if (payload.length() >= 2)
736 code = (payload[0] << 8);
737 code |= payload[1] & 0xFF;
741 ((code > 1003) && (code < 1007)) ||
742 ((code > 1011) && (code < 3000)) ||
745 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Invalid close code received");
750 QString closeMessage;
751 if (payload.length() > 2)
753 QByteArray messageBytes = payload.mid(2);
756 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Message is not valid UTF-8");
760 closeMessage = QString(messageBytes);
763 LOG(VB_HTTP, LOG_INFO, QString(
"WebSocketWorker - Received CLOSE frame - [%1] %2")
764 .
arg(QString::number(code)).
arg(closeMessage));
769 const QByteArray &payload)
773 int payloadSize = payload.length();
775 if (payloadSize >= qPow(2,64))
777 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::CreateFrame() - Payload "
778 "exceeds the allowed size for a single frame");
786 if (payloadSize > 125)
788 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::CreateFrame() - Control frames MUST NOT have payload greater than 125bytes");
795 uint64_t superHeader = 0;
800 header |= (
type << 8);
802 if (payloadSize < 126)
804 header |= payloadSize;
805 frame.insert(2, payload.constData(), payloadSize);
807 else if (payloadSize <= 65535)
810 extendedHeader = payloadSize;
811 frame.insert(4, payload.constData(), payloadSize);
816 superHeader = payloadSize;
817 frame.insert(10, payload.constData(), payloadSize);
820 frame[0] = (uint8_t)((header >> 8) & 0xFF);
821 frame[1] = (uint8_t)(header & 0xFF);
822 if (extendedHeader > 0)
824 frame[2] = (extendedHeader >> 8) & 0xFF;
825 frame[3] = extendedHeader & 0xFF;
827 else if (superHeader > 0)
829 frame[2] = (superHeader >> 56) & 0xFF;
830 frame[3] = (superHeader >> 48) & 0xFF;
831 frame[4] = (superHeader >> 40) & 0xFF;
832 frame[5] = (superHeader >> 32) & 0xFF;
833 frame[6] = (superHeader >> 24) & 0xFF;
834 frame[7] = (superHeader >> 16) & 0xFF;
835 frame[8] = (superHeader >> 8) & 0xFF;
836 frame[9] = (superHeader & 0xFF);
839 LOG(VB_HTTP, LOG_DEBUG, QString(
"WebSocketWorker::CreateFrame() - Frame size %1").
arg(QString::number(frame.length())));
852 LOG(VB_HTTP, LOG_DEBUG, QString(
"WebSocketWorker::SendFrame() - '%1'...").
arg(QString(frame.left(64).toHex())));
854 m_socket->write(frame.constData(), frame.length());
861 return SendText(message.trimmed().toUtf8());
868 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::SendText('%1...') - "
869 "Text contains invalid UTF-8 character codes. Discarded.")
870 .
arg(QString(message.left(20))));
876 return !frame.isEmpty() &&
SendFrame(frame);
883 return !frame.isEmpty() &&
SendFrame(frame);
890 return !frame.isEmpty() &&
SendFrame(frame);
897 return !frame.isEmpty() &&
SendFrame(frame);
901 const QString &message)
903 LOG(VB_HTTP, LOG_DEBUG,
"WebSocketWorker::SendClose()");
906 payload[0] = (uint8_t)(errCode >> 8);
907 payload[1] = (uint8_t)(errCode & 0xFF);
909 if (!message.isEmpty())
910 payload.append(message.toUtf8());
914 if (!frame.isEmpty() &&
SendFrame(frame))
949 if ((*it) == extension)