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 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
334 QStringList tokens = QString(line).split(
' ', QString::SkipEmptyParts);
336 QStringList tokens = QString(line).split(
' ', Qt::SkipEmptyParts);
339 if (tokens.length() != 3)
341 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Invalid number of tokens in Request line");
345 if (tokens[0] !=
"GET")
347 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Invalid method: %1").arg(tokens[0]));
351 QString path = tokens[1];
353 if (path.contains(
'#'))
355 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Path contains illegal fragment");
359 if (!tokens[2].startsWith(
"HTTP/"))
361 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Invalid protocol: %1").arg(tokens[2]));
365 QString versionStr = tokens[2].section(
'/', 1, 1);
367 int majorVersion = versionStr.section(
'.', 0, 0).toInt();
368 int minorVersion = versionStr.section(
'.', 1, 1).toInt();
370 if ((majorVersion < 1) || (minorVersion < 1))
372 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Invalid HTTP version: %1.%2").arg(majorVersion).arg(minorVersion));
377 line = socket->readLine(2000).trimmed();
379 QMap<QString, QString> requestHeaders;
380 while (!line.isEmpty())
382 QString strLine = QString(line);
383 QString sName = strLine.section(
':', 0, 0 ).trimmed();
384 QString sValue = strLine.section(
':', 1 ).trimmed();
385 sValue.replace(
" ",
"");
387 if (!sName.isEmpty() && !sValue.isEmpty())
388 requestHeaders.insert(sName.toLower(), sValue);
390 line = socket->readLine(2000).trimmed();
394 QMap<QString, QString>::iterator it;
395 for ( it = requestHeaders.begin();
396 it != requestHeaders.end();
399 LOG(VB_HTTP, LOG_INFO, QString(
"(Request Header) %1: %2")
400 .arg(it.key(), *it));
403 if (!requestHeaders.contains(
"connection"))
405 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing 'Connection' header");
409 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
410 QStringList connectionValues = requestHeaders[
"connection"].split(
',', QString::SkipEmptyParts);
412 QStringList connectionValues = requestHeaders[
"connection"].split(
',', Qt::SkipEmptyParts);
414 if (!connectionValues.contains(
"Upgrade", Qt::CaseInsensitive))
416 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Invalid 'Connection' header");
420 if (!requestHeaders.contains(
"upgrade") ||
421 requestHeaders[
"upgrade"].toLower() !=
"websocket")
423 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing or invalid 'Upgrade' header");
427 if (!requestHeaders.contains(
"sec-websocket-version"))
429 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing 'Sec-WebSocket-Version' header");
433 if (requestHeaders[
"sec-websocket-version"] !=
"13")
435 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessHandshake() - Unsupported WebSocket protocol "
436 "version. We speak '13' the client speaks '%1'")
437 .arg(requestHeaders[
"sec-websocket-version"]));
441 if (!requestHeaders.contains(
"sec-websocket-key") ||
442 QByteArray::fromBase64(requestHeaders[
"sec-websocket-key"].toLatin1()).length() != 16)
444 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessHandshake() - Missing or invalid 'sec-websocket-key' header");
450 if (requestHeaders.contains(
"user-agent") &&
451 requestHeaders[
"user-agent"].contains(
"AutobahnTestSuite"))
454 QString key = requestHeaders[
"sec-websocket-key"];
455 const QString magicString =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
458 QString acceptToken = QCryptographicHash::hash(key.append(magicString).toUtf8(),
459 QCryptographicHash::Sha1).toBase64();
461 QMap<QString, QString> responseHeaders;
462 responseHeaders.insert(
"Connection",
"Upgrade");
463 responseHeaders.insert(
"Upgrade",
"websocket");
464 responseHeaders.insert(
"Sec-WebSocket-Accept", acceptToken);
466 socket->write(
"HTTP/1.1 101 Switching Protocols\r\n");
468 QString header(
"%1: %2\r\n");
469 for (it = responseHeaders.begin(); it != responseHeaders.end(); ++it)
471 socket->write(header.arg(it.key(), *it).toLatin1());
472 LOG(VB_HTTP, LOG_INFO, QString(
"(Response Header) %1: %2")
473 .arg(it.key(), *it));
476 socket->write(
"\r\n");
477 socket->waitForBytesWritten();
489 while (
m_isRunning && socket && socket->bytesAvailable() >= 2)
491 uint8_t headerSize = 2;
493 QByteArray header = socket->peek(headerSize);
499 if (header.at(0) & 0x70)
501 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() "
502 "- Invalid data in reserved fields");
507 uint8_t opCode = (header.at(0) & 0xF);
512 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::ProcessFrames() "
513 "- Invalid OpCode (%1)")
514 .arg(QString::number(opCode, 16)));
519 frame.
m_isMasked = (((header.at(1) >> 7) & 0xFE) != 0);
528 uint8_t payloadHeaderSize = 2;
530 payloadHeaderSize = 8;
532 headerSize += payloadHeaderSize;
534 if (socket->bytesAvailable() < headerSize)
537 header = socket->peek(headerSize);
538 QByteArray payloadHeader = header.mid(2,payloadHeaderSize);
540 for (
int i = 0; i < payloadHeaderSize; i++)
542 frame.
m_payloadSize |= ((uint8_t)payloadHeader.at(i) << ((payloadHeaderSize - (i + 1)) * 8));
547 if (socket->bytesAvailable() < headerSize)
549 header = socket->peek(headerSize);
552 while ((uint64_t)socket->bytesAvailable() < (frame.
m_payloadSize + header.length()))
554 if (!socket->waitForReadyRead(2000))
561 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() - Timed out waiting for rest of frame to arrive.");
571 LOG(VB_HTTP, LOG_DEBUG, QString(
"Read Header: %1").arg(QString(header.toHex())));
572 LOG(VB_HTTP, LOG_DEBUG, QString(
"Final Frame: %1").arg(frame.
m_finalFrame ?
"Yes" :
"No"));
573 LOG(VB_HTTP, LOG_DEBUG, QString(
"Op Code: %1").arg(QString::number(frame.
m_opCode)));
574 LOG(VB_HTTP, LOG_DEBUG, QString(
"Payload Size: %1 Bytes").arg(QString::number(frame.
m_payloadSize)));
580 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() - Frame payload larger than limit of 1MB");
588 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::ProcessFrames() - Total payload larger than limit of 4MB");
593 socket->read(headerSize);
600 frame.
m_mask = header.right(4);
609 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Incomplete multi-part frame? Expected continuation.");
634 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Received Continuation Frame out of sequence");
666 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Received Unknown Frame Type");
687 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Message is not valid UTF-8");
696 if (extension->HandleTextFrame(frame))
705 if (extension->HandleBinaryFrame(frame))
730 if (payload.length() == 1)
732 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Invalid close payload");
737 if (payload.length() >= 2)
739 code = (payload[0] << 8);
740 code |= payload[1] & 0xFF;
744 ((code > 1003) && (code < 1007)) ||
745 ((code > 1011) && (code < 3000)) ||
748 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Invalid close code received");
753 QString closeMessage;
754 if (payload.length() > 2)
756 QByteArray messageBytes = payload.mid(2);
759 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Message is not valid UTF-8");
763 closeMessage = QString(messageBytes);
766 LOG(VB_HTTP, LOG_INFO, QString(
"WebSocketWorker - Received CLOSE frame - [%1] %2")
767 .arg(QString::number(code), closeMessage));
772 const QByteArray &payload)
776 int payloadSize = payload.length();
778 if (payloadSize >= std::pow(2,64))
780 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::CreateFrame() - Payload "
781 "exceeds the allowed size for a single frame");
789 if (payloadSize > 125)
791 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::CreateFrame() - Control frames MUST NOT have payload greater than 125bytes");
798 uint64_t superHeader = 0;
803 header |= (
type << 8);
805 if (payloadSize < 126)
807 header |= payloadSize;
808 frame.insert(2, payload.constData(), payloadSize);
810 else if (payloadSize <= 65535)
813 extendedHeader = payloadSize;
814 frame.insert(4, payload.constData(), payloadSize);
819 superHeader = payloadSize;
820 frame.insert(10, payload.constData(), payloadSize);
823 frame[0] = (uint8_t)((header >> 8) & 0xFF);
824 frame[1] = (uint8_t)(header & 0xFF);
825 if (extendedHeader > 0)
827 frame[2] = (extendedHeader >> 8) & 0xFF;
828 frame[3] = extendedHeader & 0xFF;
830 else if (superHeader > 0)
832 frame[2] = (superHeader >> 56) & 0xFF;
833 frame[3] = (superHeader >> 48) & 0xFF;
834 frame[4] = (superHeader >> 40) & 0xFF;
835 frame[5] = (superHeader >> 32) & 0xFF;
836 frame[6] = (superHeader >> 24) & 0xFF;
837 frame[7] = (superHeader >> 16) & 0xFF;
838 frame[8] = (superHeader >> 8) & 0xFF;
839 frame[9] = (superHeader & 0xFF);
842 LOG(VB_HTTP, LOG_DEBUG, QString(
"WebSocketWorker::CreateFrame() - Frame size %1").arg(QString::number(frame.length())));
855 LOG(VB_HTTP, LOG_DEBUG, QString(
"WebSocketWorker::SendFrame() - '%1'...").arg(QString(frame.left(64).toHex())));
857 m_socket->write(frame.constData(), frame.length());
864 return SendText(message.trimmed().toUtf8());
871 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::SendText('%1...') - "
872 "Text contains invalid UTF-8 character codes. Discarded.")
873 .arg(QString(message.left(20))));
879 return !frame.isEmpty() &&
SendFrame(frame);
886 return !frame.isEmpty() &&
SendFrame(frame);
893 return !frame.isEmpty() &&
SendFrame(frame);
900 return !frame.isEmpty() &&
SendFrame(frame);
904 const QString &message)
906 LOG(VB_HTTP, LOG_DEBUG,
"WebSocketWorker::SendClose()");
909 payload[0] = (uint8_t)(errCode >> 8);
910 payload[1] = (uint8_t)(errCode & 0xFF);
912 if (!message.isEmpty())
913 payload.append(message.toUtf8());
917 if (!frame.isEmpty() &&
SendFrame(frame))
952 if ((*it) == extension)