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 const 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 |= ((uint64_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");
659 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Received Unknown Frame Type");
680 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Message is not valid UTF-8");
687 for (
auto *
const extension : std::as_const(
m_extensions))
689 if (extension->HandleTextFrame(frame))
696 for (
auto *
const extension : std::as_const(
m_extensions))
698 if (extension->HandleBinaryFrame(frame))
723 if (payload.length() == 1)
725 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Invalid close payload");
730 if (payload.length() >= 2)
732 code = (payload[0] << 8);
733 code |= payload[1] & 0xFF;
737 ((code > 1003) && (code < 1007)) ||
738 ((code > 1011) && (code < 3000)) ||
741 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Invalid close code received");
746 QString closeMessage;
747 if (payload.length() > 2)
749 QByteArray messageBytes = payload.mid(2);
752 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker - Message is not valid UTF-8");
756 closeMessage = QString(messageBytes);
759 LOG(VB_HTTP, LOG_INFO, QString(
"WebSocketWorker - Received CLOSE frame - [%1] %2")
760 .arg(QString::number(code), closeMessage));
765 const QByteArray &payload)
769 int payloadSize = payload.length();
771 if (payloadSize >= std::pow(2,64))
773 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::CreateFrame() - Payload "
774 "exceeds the allowed size for a single frame");
782 if (payloadSize > 125)
784 LOG(VB_GENERAL, LOG_ERR,
"WebSocketWorker::CreateFrame() - Control frames MUST NOT have payload greater than 125bytes");
791 uint64_t superHeader = 0;
796 header |= (
type << 8);
798 if (payloadSize < 126)
800 header |= payloadSize;
801 frame.insert(2, payload.constData(), payloadSize);
803 else if (payloadSize <= 65535)
806 extendedHeader = payloadSize;
807 frame.insert(4, payload.constData(), payloadSize);
812 superHeader = payloadSize;
813 frame.insert(10, payload.constData(), payloadSize);
816 frame[0] = (uint8_t)((header >> 8) & 0xFF);
817 frame[1] = (uint8_t)(header & 0xFF);
818 if (extendedHeader > 0)
820 frame[2] = (extendedHeader >> 8) & 0xFF;
821 frame[3] = extendedHeader & 0xFF;
823 else if (superHeader > 0)
825 frame[2] = (superHeader >> 56) & 0xFF;
826 frame[3] = (superHeader >> 48) & 0xFF;
827 frame[4] = (superHeader >> 40) & 0xFF;
828 frame[5] = (superHeader >> 32) & 0xFF;
829 frame[6] = (superHeader >> 24) & 0xFF;
830 frame[7] = (superHeader >> 16) & 0xFF;
831 frame[8] = (superHeader >> 8) & 0xFF;
832 frame[9] = (superHeader & 0xFF);
835 LOG(VB_HTTP, LOG_DEBUG, QString(
"WebSocketWorker::CreateFrame() - Frame size %1").arg(QString::number(frame.length())));
848 LOG(VB_HTTP, LOG_DEBUG, QString(
"WebSocketWorker::SendFrame() - '%1'...").arg(QString(frame.left(64).toHex())));
850 m_socket->write(frame.constData(), frame.length());
857 return SendText(message.trimmed().toUtf8());
864 LOG(VB_GENERAL, LOG_ERR, QString(
"WebSocketWorker::SendText('%1...') - "
865 "Text contains invalid UTF-8 character codes. Discarded.")
866 .arg(QString(message.left(20))));
872 return !frame.isEmpty() &&
SendFrame(frame);
879 return !frame.isEmpty() &&
SendFrame(frame);
886 return !frame.isEmpty() &&
SendFrame(frame);
893 return !frame.isEmpty() &&
SendFrame(frame);
897 const QString &message)
899 LOG(VB_HTTP, LOG_DEBUG,
"WebSocketWorker::SendClose()");
902 payload[0] = (uint8_t)(errCode >> 8);
903 payload[1] = (uint8_t)(errCode & 0xFF);
905 if (!message.isEmpty())
906 payload.append(message.toUtf8());
910 if (!frame.isEmpty() &&
SendFrame(frame))
945 if ((*it) == extension)