4 using namespace std::chrono_literals;
17 #define LOC QString("WS: ")
43 m_formatForProtocol(
MythWS::FrameFormatForProtocol(Protocol)),
44 m_preferRawText(
MythWS::UseRawTextForProtocol(Protocol)),
46 m_timer(Testing ? nullptr : new QTimer()),
53 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Websocket setup: Protocol:%1 (Raw text: %2) Testing:%3")
66 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
67 delete m_utf8CodecState;
115 bool control = (code & 0x8) != 0;
122 errormsg = QStringLiteral(
"Invalid use of reserved bits");
127 errormsg = QStringLiteral(
"Invalid use of reserved opcode");
132 errormsg = QStringLiteral(
"Masking error");
137 errormsg = QStringLiteral(
"Control frame payload too large");
143 else if (control && !
final)
145 errormsg = QStringLiteral(
"Fragmented control frame");
150 errormsg = QStringLiteral(
"Fragmentation error (no opening frame)");
155 errormsg = QStringLiteral(
"Fragmentation error (bad frame)");
162 errormsg = QStringLiteral(
"Received incorrect frame type for subprotocol");
165 if (!errormsg.isEmpty())
167 LOG(VB_GENERAL, LOG_ERR,
LOC + errormsg);
198 auto size = qFromBigEndian<quint16>(
reinterpret_cast<const void *
>(length.data()));
209 auto size = qFromBigEndian<quint64>(
reinterpret_cast<const void *
>(length.data()));
217 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Frame size is too large");
235 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No frame");
243 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No payload allocated");
248 auto available =
m_socket->bytesAvailable();
249 auto bytesneeded = payload->size() - payload->m_readPosition;
251 if (bytesneeded > 0 && available > 0)
254 bytesneeded > available ? available : bytesneeded);
257 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Read error");
261 payload->m_readPosition +=
read;
263 LOG(VB_HTTP, LOG_DEBUG,
LOC + QString(
"Payload read: %1 (%2 of %3)")
264 .arg(
read).arg(payload->m_readPosition).arg(payload->size()));
274 for (
int i = 0; i < payload->size(); ++i)
306 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
307 (void)m_utf8Codec->toUnicode(payload->data(), payload->size(), m_utf8CodecState);
314 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
315 (*m_string).append(m_utf8Codec->toUnicode(payload->data(), payload->size(), m_utf8CodecState));
317 (*m_string).append(
m_toUtf16->decode(*payload));
322 #
if QT_VERSION < QT_VERSION_CHECK(6,0,0)
323 m_utf8CodecState->invalidChars
329 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid UTF-8");
346 LOG(VB_HTTP, LOG_ERR,
LOC +
"Message size is too large");
352 LOG(VB_HTTP, LOG_DEBUG,
LOC + QString(
"%1 frame: Final %2 Length: %3 Masked: %4 Valid: %5")
379 #
if QT_VERSION < QT_VERSION_CHECK(6,0,0)
380 m_utf8CodecState->remainingChars
386 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid UTF-8");
390 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
391 delete m_utf8CodecState;
392 m_utf8CodecState =
new QTextCodec::ConverterState;
430 }
while (
m_socket->bytesAvailable());
435 auto buffered =
m_socket->bytesToWrite();
438 LOG(VB_HTTP, LOG_DEBUG,
LOC + QString(
"Socket sent %1bytes (still to send %2)")
439 .arg(Written).arg(buffered));
445 auto seconds =
static_cast<double>(
m_writeTime.nsecsElapsed()) / 1000000000.0;
446 auto rate =
static_cast<uint64_t
>(
static_cast<double>(
m_writeTotal) / seconds);
447 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Wrote %1bytes in %2seconds (%3)")
466 LOG(VB_HTTP, LOG_DEBUG,
LOC +
"Draining buffers");
474 auto written = payload->m_writePosition;
475 auto towrite =
static_cast<int64_t
>(payload->size()) - written;
476 towrite = std::min(towrite, available);
477 auto wrote =
m_socket->write(payload->constData() + written, towrite);
483 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Write error");
488 payload->m_writePosition += wrote;
493 if (payload->m_writePosition >= payload->size())
536 if ((*Payload).size() == 1)
540 else if ((*Payload).size() > 1)
543 if ((*Payload).size() > 2)
545 auto utf8 = (*Payload).mid(2);
546 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
547 QTextCodec::ConverterState state;
548 (void)m_utf8Codec->toUnicode(utf8.constData(), utf8.size(), &state);
549 if (state.invalidChars || state.remainingChars)
560 auto code = qFromBigEndian<quint16>(
reinterpret_cast<void*
>(Payload->data()));
561 if ((code < 1000) || ((code > 1003) && (code < 1007)) || ((code > 1011) && (code < 3000)) ||
564 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Invalid close code");
584 (*header)[0] =
static_cast<int8_t
>((Code >> 8) & 0xff);
585 (*header)[1] =
static_cast<int8_t
>(Code & 0xff);
597 auto addNext = [](int64_t acc,
const auto & payload)
598 {
return payload.get() ? acc + payload->size() : acc; };
599 int64_t length = std::accumulate(Payloads.cbegin(), Payloads.cend(), 0, addNext);
602 if ((Code & 0x8) && (length > 125))
604 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Control frame payload is too large");
610 header->data()[0] =
static_cast<int8_t
>(Code | 0x80);
611 header->data()[1] =
static_cast<int8_t
>(0);
618 for (
int i = 0; i < 4; ++i)
622 header->data()[1] |= 0x80;
629 header->data()[1] |=
static_cast<int8_t
>(length);
631 else if (length <= 0xffff)
633 header->data()[1] |= 126;
635 *
reinterpret_cast<quint16*
>(size->data()) = qToBigEndian(
static_cast<quint16
>(length));
639 header->data()[1] |= 127;
641 *
reinterpret_cast<quint64*
>(size->data()) = qToBigEndian(
static_cast<quint64
>(length));
652 for (
const auto & payload: Payloads)
655 LOG(VB_HTTP, LOG_DEBUG,
LOC + QString(
"Queued %1 frame: payload size %2 (queue length: %3)")
672 auto sizein = Payload.get() ? Payload.get()->size() : 0;
674 if ((sizein == sizeout) && sizeout > 0)
677 LOG(VB_HTTP, LOG_DEBUG,
LOC +
"Unexpected pong payload (this may not be an error)");
678 LOG(VB_HTTP, LOG_DEBUG,
LOC + QString(
"Last Payload is (%1), Pong Payload is (%2)")