18 #include <QCryptographicHash>
29 m_threadPool(
"WebSocketServerPool")
31 setObjectName(
"WebSocketServer");
33 int maxThreads = std::max(QThread::idealThreadCount() * 2, 2);
39 LOG(VB_HTTP, LOG_NOTICE, QString(
"WebSocketServer(): Max Thread Count %1")
56 auto *server = qobject_cast<PrivTcpServer *>(QObject::sender());
58 type = server->GetServerType();
66 QString(
"WebSocketServer%1").arg(socket));
76 ,
const QSslConfiguration& sslConfig
80 m_webSocketServer(webSocketServer), m_socketFD(sock),
81 m_connectionType(
type)
83 , m_sslConfig(sslConfig)
98 worker->deleteLater();
107 #ifndef QT_NO_OPENSSL
108 ,
const QSslConfiguration& sslConfig
111 : m_eventLoop(new QEventLoop()),
112 m_webSocketServer(webSocketServer),
113 m_socketFD(sock), m_connectionType(
type),
114 m_heartBeat(new QTimer())
115 #ifndef QT_NO_OPENSSL
116 , m_sslConfig(sslConfig)
119 setObjectName(QString(
"WebSocketWorker(%1)")
121 LOG(VB_HTTP, LOG_INFO, QString(
"WebSocketWorker(%1): New connection")
141 extension->deleteLater();
159 LOG(VB_HTTP, LOG_INFO,
"WebSocketWorker::CloseConnection()");
169 #ifndef QT_NO_OPENSSL
170 auto *pSslSocket =
new QSslSocket();
171 if (pSslSocket->setSocketDescriptor(
m_socketFD)
175 pSslSocket->startServerEncryption();
176 if (pSslSocket->waitForEncrypted(5000))
178 LOG(VB_HTTP, LOG_INFO,
"SSL Handshake occurred, connection encrypted");
179 LOG(VB_HTTP, LOG_INFO, QString(
"Using %1 cipher").arg(pSslSocket->sessionCipher().name()));
183 LOG(VB_HTTP, LOG_WARNING,
"SSL Handshake FAILED, connection terminated");
185 pSslSocket =
nullptr;
191 pSslSocket =
nullptr;
215 m_socket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
228 if (
m_socket->error() != QAbstractSocket::UnknownSocketError)
230 LOG(VB_HTTP, LOG_WARNING, QString(
"WebSocketWorker(%1): Error %2 (%3)")
236 std::chrono::milliseconds writeTimeout = 5s;
240 m_socket->state() == QAbstractSocket::ConnectedState &&
243 LOG(VB_HTTP, LOG_DEBUG, QString(
"WebSocketWorker(%1): "
244 "Waiting for %2 bytes to be written "
245 "before closing the connection.")
257 if (!
m_socket->waitForBytesWritten(writeTimeout.count()))
259 LOG(VB_GENERAL, LOG_WARNING, QString(
"WebSocketWorker(%1): "
260 "Timed out waiting to write bytes to "
261 "the socket, waited %2 seconds")
263 .arg(writeTimeout.count() / 1000));
270 LOG(VB_HTTP, LOG_WARNING, QString(
"WebSocketWorker(%1): "
271 "Failed to write %2 bytes to "
278 LOG(VB_HTTP, LOG_INFO, QString(
"WebSocketWorker(%1): Connection %2 closed.")
280 .arg(
m_socket->socketDescriptor()));
310 LOG(VB_HTTP, LOG_WARNING,
"WebSocketServer: Timed out waiting for connection upgrade");
321 while (socket->bytesAvailable() < 16)
323 if (!socket->waitForReadyRead(5000))
329 line = socket->readLine(255).trimmed();
333 QStringList tokens = QString(line).split(
' ', Qt::SkipEmptyParts);
334 if (tokens.length() != 3)
336 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Invalid number of tokens in Request line");
340 if (tokens[0] !=
"GET")
342 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Invalid method: %1").arg(tokens[0]));
346 QString path = tokens[1];
348 if (path.contains(
'#'))
350 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Path contains illegal fragment");
354 if (!tokens[2].startsWith(
"HTTP/"))
356 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Invalid protocol: %1").arg(tokens[2]));
360 QString versionStr = tokens[2].section(
'/', 1, 1);
362 int majorVersion = versionStr.section(
'.', 0, 0).toInt();
363 int minorVersion = versionStr.section(
'.', 1, 1).toInt();
365 if ((majorVersion < 1) || (minorVersion < 1))
367 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Invalid HTTP version: %1.%2").arg(majorVersion).arg(minorVersion));
372 line = socket->readLine(2000).trimmed();
374 QMap<QString, QString> requestHeaders;
375 while (!line.isEmpty())
377 QString strLine = QString(line);
378 QString sName = strLine.section(
':', 0, 0 ).trimmed();
379 QString sValue = strLine.section(
':', 1 ).trimmed();
380 sValue.replace(
" ",
"");
382 if (!sName.isEmpty() && !sValue.isEmpty())
383 requestHeaders.insert(sName.toLower(), sValue);
385 line = socket->readLine(2000).trimmed();
389 QMap<QString, QString>::iterator it;
390 for ( it = requestHeaders.begin();
391 it != requestHeaders.end();
394 LOG(VB_HTTP, LOG_INFO, QString(
"(Request Header) %1: %2")
395 .arg(it.key(), *it));
398 if (!requestHeaders.contains(
"connection"))
400 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing 'Connection' header");
404 QStringList connectionValues = requestHeaders[
"connection"].split(
',', Qt::SkipEmptyParts);
405 if (!connectionValues.contains(
"Upgrade", Qt::CaseInsensitive))
407 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Invalid 'Connection' header");
411 if (!requestHeaders.contains(
"upgrade") ||
412 requestHeaders[
"upgrade"].toLower() !=
"websocket")
414 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing or invalid 'Upgrade' header");
418 if (!requestHeaders.contains(
"sec-websocket-version"))
420 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing 'Sec-WebSocket-Version' header");
424 if (requestHeaders[
"sec-websocket-version"] !=
"13")
426 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Unsupported WebSocket protocol "
427 "version. We speak '13' the client speaks '%1'")
428 .arg(requestHeaders[
"sec-websocket-version"]));
432 if (!requestHeaders.contains(
"sec-websocket-key") ||
433 QByteArray::fromBase64(requestHeaders[
"sec-websocket-key"].toLatin1()).length() != 16)
435 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing or invalid 'sec-websocket-key' header");
441 if (requestHeaders.contains(
"user-agent") &&
442 requestHeaders[
"user-agent"].contains(
"AutobahnTestSuite"))
445 QString key = requestHeaders[
"sec-websocket-key"];
446 const QString magicString =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
449 QString acceptToken = QCryptographicHash::hash(key.append(magicString).toUtf8(),
450 QCryptographicHash::Sha1).toBase64();
452 QMap<QString, QString> responseHeaders;
453 responseHeaders.insert(
"Connection",
"Upgrade");
454 responseHeaders.insert(
"Upgrade",
"websocket");
455 responseHeaders.insert(
"Sec-WebSocket-Accept", acceptToken);
457 socket->write(
"HTTP/1.1 101 Switching Protocols\r\n");
459 QString header(
"%1: %2\r\n");
460 for (it = responseHeaders.begin(); it != responseHeaders.end(); ++it)
462 socket->write(header.arg(it.key(), *it).toLatin1());
463 LOG(VB_HTTP, LOG_INFO, QString(
"(Response Header) %1: %2")
464 .arg(it.key(), *it));
467 socket->write(
"\r\n");
468 socket->waitForBytesWritten();
480 while (
m_isRunning && socket && socket->bytesAvailable() >= 2)
482 uint8_t headerSize = 2;
484 QByteArray header = socket->peek(headerSize);
490 if (header.at(0) & 0x70)
492 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() "
493 "- Invalid data in reserved fields");
498 uint8_t opCode = (header.at(0) & 0xF);
503 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessFrames() "
504 "- Invalid OpCode (%1)")
505 .arg(QString::number(opCode, 16)));
510 frame.
m_isMasked = (((header.at(1) >> 7) & 0xFE) != 0);
519 uint8_t payloadHeaderSize = 2;
521 payloadHeaderSize = 8;
523 headerSize += payloadHeaderSize;
525 if (socket->bytesAvailable() < headerSize)
528 header = socket->peek(headerSize);
529 QByteArray payloadHeader = header.mid(2,payloadHeaderSize);
531 for (
int i = 0; i < payloadHeaderSize; i++)
533 frame.
m_payloadSize |= ((uint8_t)payloadHeader.at(i) << ((payloadHeaderSize - (i + 1)) * 8));
538 if (socket->bytesAvailable() < headerSize)
540 header = socket->peek(headerSize);
543 while ((uint64_t)socket->bytesAvailable() < (frame.
m_payloadSize + header.length()))
545 if (!socket->waitForReadyRead(2000))
552 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() - Timed out waiting for rest of frame to arrive.");
562 LOG(VB_HTTP, LOG_DEBUG, QString(
"Read Header: %1").arg(QString(header.toHex())));
563 LOG(VB_HTTP, LOG_DEBUG, QString(
"Final Frame: %1").arg(frame.
m_finalFrame ?
"Yes" :
"No"));
564 LOG(VB_HTTP, LOG_DEBUG, QString(
"Op Code: %1").arg(QString::number(frame.
m_opCode)));
565 LOG(VB_HTTP, LOG_DEBUG, QString(
"Payload Size: %1 Bytes").arg(QString::number(frame.
m_payloadSize)));
571 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() - Frame payload larger than limit of 1MB");
579 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() - Total payload larger than limit of 4MB");
584 socket->read(headerSize);
591 frame.
m_mask = header.right(4);
600 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Incomplete multi-part frame? Expected continuation.");
625 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Received Continuation Frame out of sequence");
657 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Received Unknown Frame Type");
678 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Message is not valid UTF-8");
685 for (
auto *
const extension : std::as_const(
m_extensions))
687 if (extension->HandleTextFrame(frame))
694 for (
auto *
const extension : std::as_const(
m_extensions))
696 if (extension->HandleBinaryFrame(frame))
721 if (payload.length() == 1)
723 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Invalid close payload");
728 if (payload.length() >= 2)
730 code = (payload[0] << 8);
731 code |= payload[1] & 0xFF;
735 ((code > 1003) && (code < 1007)) ||
736 ((code > 1011) && (code < 3000)) ||
739 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Invalid close code received");
744 QString closeMessage;
745 if (payload.length() > 2)
747 QByteArray messageBytes = payload.mid(2);
750 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Message is not valid UTF-8");
754 closeMessage = QString(messageBytes);
757 LOG(VB_HTTP, LOG_INFO, QString(
"WebSocketWorker - Received CLOSE frame - [%1] %2")
758 .arg(QString::number(code), closeMessage));
763 const QByteArray &payload)
767 int payloadSize = payload.length();
769 if (payloadSize >= std::pow(2,64))
771 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::CreateFrame() - Payload "
772 "exceeds the allowed size for a single frame");
780 if (payloadSize > 125)
782 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::CreateFrame() - Control frames MUST NOT have payload greater than 125bytes");
789 uint64_t superHeader = 0;
794 header |= (
type << 8);
796 if (payloadSize < 126)
798 header |= payloadSize;
799 frame.insert(2, payload.constData(), payloadSize);
801 else if (payloadSize <= 65535)
804 extendedHeader = payloadSize;
805 frame.insert(4, payload.constData(), payloadSize);
810 superHeader = payloadSize;
811 frame.insert(10, payload.constData(), payloadSize);
814 frame[0] = (uint8_t)((header >> 8) & 0xFF);
815 frame[1] = (uint8_t)(header & 0xFF);
816 if (extendedHeader > 0)
818 frame[2] = (extendedHeader >> 8) & 0xFF;
819 frame[3] = extendedHeader & 0xFF;
821 else if (superHeader > 0)
823 frame[2] = (superHeader >> 56) & 0xFF;
824 frame[3] = (superHeader >> 48) & 0xFF;
825 frame[4] = (superHeader >> 40) & 0xFF;
826 frame[5] = (superHeader >> 32) & 0xFF;
827 frame[6] = (superHeader >> 24) & 0xFF;
828 frame[7] = (superHeader >> 16) & 0xFF;
829 frame[8] = (superHeader >> 8) & 0xFF;
830 frame[9] = (superHeader & 0xFF);
833 LOG(VB_HTTP, LOG_DEBUG, QString(
"WebSocketWorker::CreateFrame() - Frame size %1").arg(QString::number(frame.length())));
846 LOG(VB_HTTP, LOG_DEBUG, QString(
"WebSocketWorker::SendFrame() - '%1'...").arg(QString(frame.left(64).toHex())));
848 m_socket->write(frame.constData(), frame.length());
855 return SendText(message.trimmed().toUtf8());
862 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::SendText('%1...') - "
863 "Text contains invalid UTF-8 character codes. Discarded.")
864 .arg(QString(message.left(20))));
870 return !frame.isEmpty() &&
SendFrame(frame);
877 return !frame.isEmpty() &&
SendFrame(frame);
884 return !frame.isEmpty() &&
SendFrame(frame);
891 return !frame.isEmpty() &&
SendFrame(frame);
895 const QString &message)
897 LOG(VB_HTTP, LOG_DEBUG,
"WebSocketWorker::SendClose()");
900 payload[0] = (uint8_t)(errCode >> 8);
901 payload[1] = (uint8_t)(errCode & 0xFF);
903 if (!message.isEmpty())
904 payload.append(message.toUtf8());
908 if (!frame.isEmpty() &&
SendFrame(frame))
943 if ((*it) == extension)