MythTV  master
mythwebsocket.cpp
Go to the documentation of this file.
1 // Std
2 #include <algorithm>
3 #include <chrono>
4 using namespace std::chrono_literals;
5 #include <limits> // workaround QTBUG-90395
6 
7 // Qt
8 #include <QThread>
9 #include <QtEndian>
10 #include <QTcpSocket>
11 
12 // MythTV
13 #include "mythlogging.h"
14 #include "mythrandom.h"
15 #include "http/mythwebsocket.h"
16 
17 #define LOC QString("WS: ")
18 static constexpr int64_t MAX_FRAME_SIZE { 1048576 }; // 1MB
19 static constexpr int64_t MAX_MESSAGE_SIZE { 4194304 }; // 4MB
20 
32 MythWebSocket* MythWebSocket::CreateWebsocket(bool Server, QTcpSocket *Socket,
33  MythSocketProtocol Protocol, bool Testing)
34 {
35  if (Socket && (MythSocketProtocol::ProtHTTP != Protocol))
36  return new MythWebSocket(Server, Socket, Protocol, Testing);
37  return nullptr;
38 }
39 
40 MythWebSocket::MythWebSocket(bool Server, QTcpSocket *Socket, MythSocketProtocol Protocol, bool Testing)
41  : m_socket(Socket),
42  m_protocol(Protocol),
43  m_formatForProtocol(MythWS::FrameFormatForProtocol(Protocol)),
44  m_preferRawText(MythWS::UseRawTextForProtocol(Protocol)),
45  m_testing(Testing),
46  m_timer(Testing ? nullptr : new QTimer()),
47  m_serverSide(Server)
48 {
49  // Connect read/write signals
50  connect(m_socket, &QTcpSocket::readyRead, this, &MythWebSocket::Read);
51  connect(m_socket, &QTcpSocket::bytesWritten, this, &MythWebSocket::Write);
52 
53  LOG(VB_HTTP, LOG_INFO, LOC + QString("Websocket setup: Protocol:%1 (Raw text: %2) Testing:%3")
55  .arg(m_preferRawText).arg(m_testing));
56 
57  if (m_timer)
58  {
60  m_timer->start(20s);
61  }
62 }
63 
65 {
66 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
67  delete m_utf8CodecState;
68 #else
69  delete m_toUtf16;
70 #endif
71  delete m_timer;
72 }
73 
82 {
83  SendClose(WSCloseGoingAway, "Exiting");
84 }
85 
86 void MythWebSocket::SendTextFrame(const QString& Text)
87 {
89 }
90 
91 void MythWebSocket::SendBinaryFrame(const QByteArray& Data)
92 {
94 }
95 
97 {
98  QString errormsg;
99  do
100  {
101  errormsg.clear();
102  // For small frames this should process everything without looping -
103  // including zero length payloads
104  if (ReadHeader == m_readState)
105  {
106  // Header must mean a new frame
107  m_currentFrame = nullptr;
108 
109  // we need at least 2 bytes to start reading
110  if (m_socket->bytesAvailable() < 2)
111  return;
112 
114  auto code = m_currentFrame->m_opCode;
115  bool control = (code & 0x8) != 0;
116  bool fragmented = (!m_dataFragments.empty()) || (m_string.get() != nullptr);
117  bool final = m_currentFrame->m_final;
118 
119  // Invalid use of reserved bits
120  if (m_currentFrame->m_invalid)
121  {
122  errormsg = QStringLiteral("Invalid use of reserved bits");
123  }
124  // Valid opcode
125  else if (!MythWS::OpCodeIsValid(code))
126  {
127  errormsg = QStringLiteral("Invalid use of reserved opcode");
128  }
129  // Clients must be sending masked frames and vice versa
130  else if (m_serverSide != m_currentFrame->m_masked)
131  {
132  errormsg = QStringLiteral("Masking error");
133  }
134  // Invalid control frame size
135  else if (control && (m_currentFrame->m_length > 125))
136  {
137  errormsg = QStringLiteral("Control frame payload too large");
138  // need to acknowledge when an OpClose is received
139  if (WSOpClose == m_currentFrame->m_opCode)
140  m_closeReceived = true;
141  }
142  // Control frames cannot be fragmented
143  else if (control && !final)
144  {
145  errormsg = QStringLiteral("Fragmented control frame");
146  }
147  // Continuation frame must have an opening frame
148  else if (!fragmented && code == WSOpContinuation)
149  {
150  errormsg = QStringLiteral("Fragmentation error (no opening frame)");
151  }
152  // Only continuation frames or control frames are expected once in the middle of a message
153  else if (fragmented && (code != WSOpContinuation) && !control)
154  {
155  errormsg = QStringLiteral("Fragmentation error (bad frame)");
156  }
157  // ensure OpCode matches that expected by SubProtocol
158  else if ((!m_testing && m_protocol != ProtFrame) &&
159  (code == WSOpTextFrame || code == WSOpBinaryFrame) &&
160  code != m_formatForProtocol)
161  {
162  errormsg = QStringLiteral("Received incorrect frame type for subprotocol");
163  }
164 
165  if (!errormsg.isEmpty())
166  {
167  LOG(VB_GENERAL, LOG_ERR, LOC + errormsg);
168  SendClose(WSCloseProtocolError, errormsg);
169  // Invalidate the frame so it is ignored, but we must carry
170  // on to ensure we process the close reply
171  m_currentFrame->m_invalid = true;
172  }
173 
174  if (126 == m_currentFrame->m_length)
175  {
177  }
178  else if (127 == m_currentFrame->m_length)
179  {
181  }
182  else
183  {
186  }
187 
188  // Set the OpCode for the entire message in the case of fragmented messages
189  if (!final && (code == WSOpBinaryFrame || code == WSOpTextFrame))
190  m_messageOpCode = code;
191  }
192 
194  {
195  if (m_socket->bytesAvailable() < 2)
196  return;
197  auto length = m_socket->read(2);
198  auto size = qFromBigEndian<quint16>(reinterpret_cast<const void *>(length.data()));
200  m_currentFrame->m_length = size;
202  }
203 
205  {
206  if (m_socket->bytesAvailable() < 8)
207  return;
208  auto length = m_socket->read(8);
209  auto size = qFromBigEndian<quint64>(reinterpret_cast<const void *>(length.data()));
211  m_currentFrame->m_length = size;
213 
214  // Large frame sizes are not needed with current implementations
215  if (!m_testing && (size > MAX_FRAME_SIZE))
216  {
217  LOG(VB_GENERAL, LOG_ERR, LOC + "Frame size is too large");
219  m_currentFrame->m_invalid = true;
220  }
221  }
222 
223  if (ReadMask == m_readState)
224  {
225  if (m_socket->bytesAvailable() < 4)
226  return;
227  m_currentFrame->m_mask = m_socket->read(4);
229  }
230 
231  if (ReadPayload == m_readState)
232  {
233  if (!m_currentFrame.get())
234  {
235  LOG(VB_GENERAL, LOG_ERR, LOC + "No frame");
236  SendClose(WSCloseUnexpectedErr, QStringLiteral("Internal error"));
237  return;
238  }
239 
240  auto & payload = m_currentFrame->m_payload;
241  if (!payload)
242  {
243  LOG(VB_GENERAL, LOG_ERR, LOC + "No payload allocated");
244  SendClose(WSCloseUnexpectedErr, QStringLiteral("Internal error"));
245  return;
246  }
247 
248  auto available = m_socket->bytesAvailable();
249  auto bytesneeded = payload->size() - payload->m_readPosition;
250  // Note: payload size can be zero
251  if (bytesneeded > 0 && available > 0)
252  {
253  auto read = m_socket->read(payload->data() + m_currentFrame->m_payload->m_readPosition,
254  bytesneeded > available ? available : bytesneeded);
255  if (read < 0)
256  {
257  LOG(VB_GENERAL, LOG_ERR, LOC + "Read error");
258  SendClose(WSCloseUnexpectedErr, QStringLiteral("Read error"));
259  return;
260  }
261  payload->m_readPosition += read;
262  bytesneeded -= 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()));
265  }
266 
267  // Have we finished reading the current frame
268  if (bytesneeded < 1)
269  {
270  bool valid = !m_currentFrame->m_invalid;
271 
272  // unmask payload - TODO Optimise this (e.g SIMD)
273  if (valid && m_currentFrame->m_masked)
274  for (int i = 0; i < payload->size(); ++i)
275  payload->data()[i] ^= m_currentFrame->m_mask[i % 4];
276 
277  // Ensure we have the current *message* opcode
278  auto messageopcode = m_currentFrame->m_opCode;
279  if (WSOpContinuation == messageopcode)
280  messageopcode = m_messageOpCode;
281 
282  // Validate UTF-8 text on a frame by frame basis, accumulating
283  // into our string pointer. This optimises UTF-8 handling for
284  // internal purposes by converting only once but is less efficient
285  // for echo testing; as we immediately convert the QString back to UTF-8.
286  // Note: In an ideal world we would avoid reading to an intermediary
287  // buffer before converting. QTextStream notionally supports reading
288  // and converting in a single operation (and appears to support
289  // error detection in the conversion) but it can only be used to
290  // read text; its internal buffering will break direct reads
291  // from the socket (for headers, binary frames etc).
292  if (WSOpTextFrame == messageopcode)
293  {
294  m_messageSize += payload->size();
295 
296  // Note: we check invalidChars here and remainingChars
297  // when the full message has been received - as fragments
298  // may not be on code point boundaries; whatever that means :)
299  // Note: This still does not catch 3 'fail fast on UTF-8'
300  // Autobahn tests - though we still pass with 'non-strict'.
301  // I can't see a way to pass strictly without breaking
302  // other tests for valid UTF-8
303  if (m_preferRawText)
304  {
305  m_dataFragments.emplace_back(payload);
306 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
307  (void)m_utf8Codec->toUnicode(payload->data(), payload->size(), m_utf8CodecState);
308 #endif
309  }
310  else
311  {
312  if (!m_string)
314 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
315  (*m_string).append(m_utf8Codec->toUnicode(payload->data(), payload->size(), m_utf8CodecState));
316 #else
317  (*m_string).append(m_toUtf16->decode(*payload));
318 #endif
319  }
320 
321  if (
322 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
323  m_utf8CodecState->invalidChars
324 #else
325  m_toUtf16->hasError()
326 #endif
327  )
328  {
329  LOG(VB_HTTP, LOG_ERR, LOC + "Invalid UTF-8");
330  SendClose(WSCloseBadData, "Invalid UTF-8");
331  m_currentFrame->m_invalid = true;
332  valid = false;
333  }
334  }
335 
336  // Add this data payload to the message payload
337  if (WSOpBinaryFrame == messageopcode)
338  {
339  m_dataFragments.emplace_back(payload);
340  m_messageSize += payload->size();
341  }
342 
343  // Stop clients from sending large messages.
345  {
346  LOG(VB_HTTP, LOG_ERR, LOC + "Message size is too large");
348  m_currentFrame->m_invalid = true;
349  valid = false;
350  }
351 
352  LOG(VB_HTTP, LOG_DEBUG, LOC + QString("%1 frame: Final %2 Length: %3 Masked: %4 Valid: %5")
353  .arg(MythWS::OpCodeToString(m_currentFrame->m_opCode))
354  .arg(m_currentFrame->m_final).arg(m_currentFrame->m_length)
355  .arg(m_currentFrame->m_masked).arg(valid));
356 
357  // Message complete?
358  if (m_currentFrame->m_final)
359  {
360  // Control frames (no fragmentation - so only one payload)
361  if (valid && WSOpPing == m_currentFrame->m_opCode)
362  {
363  PingReceived(payload);
364  }
365  else if (valid && WSOpPong == m_currentFrame->m_opCode)
366  {
367  PongReceived(payload);
368  }
369  else if (valid && WSOpClose == m_currentFrame->m_opCode)
370  {
371  CloseReceived(payload);
372  }
373  else
374  {
375  // Final UTF-8 validation
376  if ((WSOpTextFrame == messageopcode))
377  {
378  if (
379 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
380  m_utf8CodecState->remainingChars
381 #else
382  m_toUtf16->hasError()
383 #endif
384  )
385  {
386  LOG(VB_HTTP, LOG_ERR, LOC + "Invalid UTF-8");
387  SendClose(WSCloseBadData, "Invalid UTF-8");
388  valid = false;
389  }
390 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
391  delete m_utf8CodecState;
392  m_utf8CodecState = new QTextCodec::ConverterState;
393 #else
394  delete m_toUtf16;
395  m_toUtf16 = new QStringDecoder;
396 #endif
397  }
398 
399  // Echo back to the Autobahn server
400  if (valid && m_testing)
401  {
402  if (WSOpTextFrame == messageopcode && m_preferRawText)
404  else if (WSOpTextFrame == messageopcode)
406  else if (WSOpBinaryFrame == messageopcode)
408  }
409  else if (valid)
410  {
411  if (WSOpTextFrame == messageopcode&& m_preferRawText)
413  else if (WSOpTextFrame == messageopcode)
414  emit NewTextMessage(m_string);
415  else if (WSOpBinaryFrame == messageopcode)
417  }
418 
419  // Cleanup
420  m_dataFragments.clear();
421  m_string = nullptr;
422  }
423  }
424 
425  // Next read must be a new frame with a fresh header
426  m_currentFrame = nullptr;
428  }
429  }
430  } while (m_socket->bytesAvailable());
431 }
432 
433 void MythWebSocket::Write(int64_t Written)
434 {
435  auto buffered = m_socket->bytesToWrite();
436  if (Written)
437  {
438  LOG(VB_HTTP, LOG_DEBUG, LOC + QString("Socket sent %1bytes (still to send %2)")
439  .arg(Written).arg(buffered));
440 
441  if (VERBOSE_LEVEL_CHECK(VB_HTTP, LOG_INFO) && m_writeQueue.empty() && !buffered)
442  {
443  if (m_writeTotal > 100)
444  {
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)")
448  .arg(m_writeTotal).arg(seconds, 8, 'f', 6, '0').arg(MythHTTPWS::BitrateToString(rate)));
449  }
450  m_writeTime.invalidate();
451  m_writeTotal = 0;
452  }
453  }
454 
455  if (m_writeQueue.empty())
456  return;
457 
458  if (VERBOSE_LEVEL_CHECK(VB_HTTP, LOG_INFO) && !m_writeTime.isValid())
459  m_writeTime.start();
460 
461  // If the client cannot consume data as fast as we are sending it, we just
462  // buffer more and more data in the Qt socket code. So return and wait for
463  // the next write.
464  if (buffered > (HTTP_CHUNKSIZE << 1))
465  {
466  LOG(VB_HTTP, LOG_DEBUG, LOC + "Draining buffers");
467  return;
468  }
469 
470  int64_t available = HTTP_CHUNKSIZE;
471  while ((available > 0) && !m_writeQueue.empty())
472  {
473  auto & payload = m_writeQueue.front();
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);
478 
479  if (wrote < 0)
480  {
481  // If there is a write error, there is little option other than to
482  // fail the socket
483  LOG(VB_GENERAL, LOG_ERR, LOC + "Write error");
484  m_socket->close();
485  return;
486  }
487 
488  payload->m_writePosition += wrote;
489  available -= wrote;
490  m_writeTotal += wrote;
491 
492  // Have we finished with this payload
493  if (payload->m_writePosition >= payload->size())
494  m_writeQueue.pop_front();
495  }
496 }
497 
498  /* After both sending and receiving a Close message, an endpoint
499  considers the WebSocket connection closed and MUST close the
500  underlying TCP connection. The server MUST close the underlying TCP
501  connection immediately; the client SHOULD wait for the server to
502  close the connection but MAY close the connection at any time after
503  sending and receiving a Close message, e.g., if it has not received a
504  TCP Close from the server in a reasonable time period. */
506 {
507  auto closing = m_closeReceived || m_closeSent;
508  if (closing && !m_closing)
509  {
510  // Repurpose our ping timer as a timeout for the completion of the closing handshake
511  if (m_timer)
512  {
513  m_timer->stop();
514  m_timer->disconnect();
515  }
516  else
517  {
518  m_timer = new QTimer();
519  }
521  m_timer->start(5s);
522  }
523  m_closing = closing;
524 
526  m_socket->close();
527 }
528 
530 {
531  m_closeReceived = true;
532 
534 
535  // Incoming payload must be empty or be at least 2 bytes in size
536  if ((*Payload).size() == 1)
537  {
539  }
540  else if ((*Payload).size() > 1)
541  {
542  // Extra data *may* be available but it *should* be UTF-8 encoded
543  if ((*Payload).size() > 2)
544  {
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)
551 #else
552  (void)m_toUtf16->decode(utf8);
553  if(m_toUtf16->hasError())
555 #endif
556  }
557 
558  if (WSCloseNormal == close)
559  {
560  auto code = qFromBigEndian<quint16>(reinterpret_cast<void*>(Payload->data()));
561  if ((code < 1000) || ((code > 1003) && (code < 1007)) || ((code > 1011) && (code < 3000)) ||
562  (code > 4999))
563  {
564  LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid close code");
566  }
567  else
568  {
569  close = static_cast<WSErrorCode>(code);
570  }
571  }
572  }
573 
574  SendClose(close);
575  CheckClose();
576 }
577 
578 void MythWebSocket::SendClose(WSErrorCode Code, const QString& Message)
579 {
580  if (m_closeSent)
581  return;
582 
583  auto header = MythSharedData::CreatePayload(2);
584  (*header)[0] = static_cast<int8_t>((Code >> 8) & 0xff);
585  (*header)[1] = static_cast<int8_t>(Code & 0xff);
586  SendFrame(WSOpClose, { header, MythSharedData::CreatePayload(Message) });
587  m_closeSent = true;
588  CheckClose();
589 }
590 
591 void MythWebSocket::SendFrame(WSOpCode Code, const DataPayloads& Payloads)
592 {
593  if (m_closeSent || (m_closeReceived && (Code != WSOpClose)))
594  return;
595 
596  // Payload size (if needed)
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);
600 
601  // Payload for control frames must not exceed 125bytes
602  if ((Code & 0x8) && (length > 125))
603  {
604  LOG(VB_GENERAL, LOG_ERR, LOC + "Control frame payload is too large");
605  return;
606  }
607 
608  // Assume no fragmentation for now
609  auto header = MythSharedData::CreatePayload(2);
610  header->data()[0] = static_cast<int8_t>(Code | 0x80);
611  header->data()[1] = static_cast<int8_t>(0);
612 
613  // Add mask if necessary
614  DataPayload mask = nullptr;
615  if (!m_serverSide)
616  {
618  for (int i = 0; i < 4; ++i)
619  {
620  mask->data()[i] = (int8_t)MythRandomInt(INT8_MIN, INT8_MAX);
621  }
622  header->data()[1] |= 0x80;
623  }
624 
625  // generate correct size
626  DataPayload size = nullptr;
627  if (length < 126)
628  {
629  header->data()[1] |= static_cast<int8_t>(length);
630  }
631  else if (length <= 0xffff)
632  {
633  header->data()[1] |= 126;
635  *reinterpret_cast<quint16*>(size->data()) = qToBigEndian(static_cast<quint16>(length));
636  }
637  else
638  {
639  header->data()[1] |= 127;
641  *reinterpret_cast<quint64*>(size->data()) = qToBigEndian(static_cast<quint64>(length));
642  }
643 
644  // Queue header
645  m_writeQueue.emplace_back(header);
646  // Queue size
647  if (size.get())
648  m_writeQueue.emplace_back(size);
649  // Queue mask
650  if (mask.get())
651  m_writeQueue.emplace_back(mask);
652  for (const auto & payload: Payloads)
653  m_writeQueue.emplace_back(payload);
654 
655  LOG(VB_HTTP, LOG_DEBUG, LOC + QString("Queued %1 frame: payload size %2 (queue length: %3)")
656  .arg(MythWS::OpCodeToString(Code)).arg(length).arg(m_writeQueue.size()));
657 
658  // Kick off the write
659  Write();
660 }
661 
663 {
665  return;
666  SendFrame(WSOpPong, { std::move(Payload) });
667 }
668 
670 {
671  // Validate against the last known ping payload and warn on error
672  auto sizein = Payload.get() ? Payload.get()->size() : 0;
673  auto sizeout = m_lastPingPayload.size();
674  if ((sizein == sizeout) && sizeout > 0)
675  if (*Payload.get() == m_lastPingPayload)
676  return;
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)")
679  .arg(m_lastPingPayload.constData(), Payload.get()->constData()));
680 }
681 
683 {
684  m_lastPingPayload = QString::number(MythRandom() & 0xFFFF, 16).toUtf8();
685  if (auto payload = MythSharedData::CreatePayload(m_lastPingPayload); payload.get())
686  SendFrame(WSOpPing, { payload });
687 }
688 
MythSocketProtocol
MythSocketProtocol
Definition: mythhttpcommon.h:25
MythWebSocket::m_socket
QTcpSocket * m_socket
Definition: mythwebsocket.h:62
MythWebSocket::ReadMask
@ ReadMask
Definition: mythwebsocket.h:58
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:102
MythWebSocket::m_closeReceived
bool m_closeReceived
Definition: mythwebsocket.h:71
MythWebSocket::m_writeTime
QElapsedTimer m_writeTime
Definition: mythwebsocket.h:89
WSCloseUnexpectedErr
@ WSCloseUnexpectedErr
Definition: mythwebsockettypes.h:45
mythrandom.h
WSCloseTooLarge
@ WSCloseTooLarge
Definition: mythwebsockettypes.h:43
MythWebSocket::m_dataFragments
DataPayloads m_dataFragments
Definition: mythwebsocket.h:77
MythWebSocket::m_closing
bool m_closing
Definition: mythwebsocket.h:72
MythWebSocket::SendBinaryFrame
void SendBinaryFrame(const QByteArray &Data)
Definition: mythwebsocket.cpp:91
HTTP_CHUNKSIZE
#define HTTP_CHUNKSIZE
A group of functions shared between HTTP and WebSocket code.
Definition: mythhttpcommon.h:18
discid.disc.read
def read(device=None, features=[])
Definition: disc.py:35
MythWebSocket::Read64BitLength
@ Read64BitLength
Definition: mythwebsocket.h:57
MythSharedString::CreateString
static StringPayload CreateString()
Definition: mythhttpcommon.cpp:37
WSOpTextFrame
@ WSOpTextFrame
Definition: mythwebsockettypes.h:16
ProtHTTP
@ ProtHTTP
Definition: mythhttpcommon.h:27
VERBOSE_LEVEL_CHECK
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
MythWebSocket::~MythWebSocket
~MythWebSocket() override
Definition: mythwebsocket.cpp:64
MythWebSocket::ReadHeader
@ ReadHeader
Definition: mythwebsocket.h:55
WSOpPong
@ WSOpPong
Definition: mythwebsockettypes.h:25
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythWebSocket::Write
void Write(int64_t Written=0)
Definition: mythwebsocket.cpp:433
MythWebSocket::m_preferRawText
bool m_preferRawText
Definition: mythwebsocket.h:65
MythWebSocket::m_writeTotal
int64_t m_writeTotal
Definition: mythwebsocket.h:88
WSCloseProtocolError
@ WSCloseProtocolError
Definition: mythwebsockettypes.h:37
LOC
#define LOC
Definition: mythwebsocket.cpp:17
MythWebSocket::m_messageSize
int64_t m_messageSize
Definition: mythwebsocket.h:76
WSOpContinuation
@ WSOpContinuation
Definition: mythwebsockettypes.h:15
close
#define close
Definition: compat.h:43
MythWebSocket::m_timer
QTimer * m_timer
Definition: mythwebsocket.h:67
MythWebSocket::PongReceived
void PongReceived(const DataPayload &Payload)
Definition: mythwebsocket.cpp:669
MythWebSocket::CloseReceived
void CloseReceived(const DataPayload &Payload)
Definition: mythwebsocket.cpp:529
mythlogging.h
MythHTTPWS::ProtocolToString
static QString ProtocolToString(MythSocketProtocol Protocol)
Definition: mythhttpcommon.cpp:42
MythWebSocket::m_readState
ReadState m_readState
Definition: mythwebsocket.h:68
MythWebSocket::m_string
StringPayload m_string
Definition: mythwebsocket.h:78
MythWebSocket::SendFrame
void SendFrame(WSOpCode Code, const DataPayloads &Payloads)
Definition: mythwebsocket.cpp:591
MythWebSocket::CreateWebsocket
static MythWebSocket * CreateWebsocket(bool Server, QTcpSocket *Socket, MythSocketProtocol Protocol, bool Testing)
Definition: mythwebsocket.cpp:32
DataPayload
std::shared_ptr< MythSharedData > DataPayload
Definition: mythhttpcommon.h:36
MythWebSocket::PingReceived
void PingReceived(DataPayload Payload)
Definition: mythwebsocket.cpp:662
WSOpBinaryFrame
@ WSOpBinaryFrame
Definition: mythwebsockettypes.h:17
MythWebSocket::m_closeSent
bool m_closeSent
Definition: mythwebsocket.h:70
MythRandomStd::MythRandomInt
int MythRandomInt(int min, int max)
generate a uniformly distributed random signed int in the closed interval [min, max].
Definition: mythrandom.h:58
MythSharedData::CreatePayload
static DataPayload CreatePayload(size_t Size)
Definition: mythhttpcommon.cpp:7
WSErrorCode
WSErrorCode
Definition: mythwebsockettypes.h:33
MythWS::OpCodeIsValid
static bool OpCodeIsValid(WSOpCode Code)
Definition: mythwebsockettypes.h:79
MythWebSocket::NewBinaryMessage
void NewBinaryMessage(const DataPayloads &Payloads)
MythWebSocket::ReadPayload
@ ReadPayload
Definition: mythwebsocket.h:59
MythWebSocket::m_toUtf16
QStringDecoder * m_toUtf16
Definition: mythwebsocket.h:83
MythWebSocket::Read16BitLength
@ Read16BitLength
Definition: mythwebsocket.h:56
WSOpCode
WSOpCode
Definition: mythwebsockettypes.h:13
WSCloseBadData
@ WSCloseBadData
Definition: mythwebsockettypes.h:41
MythWS
Definition: mythwebsockettypes.h:73
MythWebSocket::m_formatForProtocol
WSOpCode m_formatForProtocol
Definition: mythwebsocket.h:64
MythWebSocket::m_protocol
MythSocketProtocol m_protocol
Definition: mythwebsocket.h:63
MythWebSocket::Read
void Read()
Definition: mythwebsocket.cpp:96
MythWebSocket::NewTextMessage
void NewTextMessage(const StringPayload &Text)
ProtFrame
@ ProtFrame
Definition: mythhttpcommon.h:28
MythWebSocket::NewRawTextMessage
void NewRawTextMessage(const DataPayloads &Payloads)
WSOpClose
@ WSOpClose
Definition: mythwebsockettypes.h:23
MythWebSocket::Close
void Close()
Initiate the close handshake when we are exiting.
Definition: mythwebsocket.cpp:81
MythHTTPWS::BitrateToString
static QString BitrateToString(uint64_t Rate)
Definition: mythhttpcommon.h:76
MAX_MESSAGE_SIZE
static constexpr int64_t MAX_MESSAGE_SIZE
Definition: mythwebsocket.cpp:19
mythwebsocket.h
MythWebSocket::m_lastPingPayload
QByteArray m_lastPingPayload
Definition: mythwebsocket.h:86
MythWebSocket::m_serverSide
bool m_serverSide
Definition: mythwebsocket.h:69
MythWebSocket::SendPing
void SendPing()
Definition: mythwebsocket.cpp:682
MythWS::OpCodeToString
static QString OpCodeToString(WSOpCode Code)
Definition: mythwebsockettypes.cpp:30
MythWebSocket::m_currentFrame
WSFrame m_currentFrame
Definition: mythwebsocket.h:74
MythWebSocket::SendTextFrame
void SendTextFrame(const QString &Text)
Definition: mythwebsocket.cpp:86
MAX_FRAME_SIZE
static constexpr int64_t MAX_FRAME_SIZE
Definition: mythwebsocket.cpp:18
WSOpPing
@ WSOpPing
Definition: mythwebsockettypes.h:24
MythWebSocketFrame::CreateFrame
static WSFrame CreateFrame(const QByteArray &Header)
Definition: mythwebsockettypes.cpp:7
MythWebSocket::m_testing
bool m_testing
Definition: mythwebsocket.h:66
MythWebSocket::MythWebSocket
MythWebSocket(bool Server, QTcpSocket *Socket, MythSocketProtocol Protocol, bool Testing)
Definition: mythwebsocket.cpp:40
MythWebSocket::CheckClose
void CheckClose()
Definition: mythwebsocket.cpp:505
MythRandomStd::MythRandom
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
MythWebSocket::m_writeQueue
WSQueue m_writeQueue
Definition: mythwebsocket.h:87
MythWebSocket
An implementation of the WebSocket protocol...
Definition: mythwebsocket.h:20
MythWebSocket::m_messageOpCode
WSOpCode m_messageOpCode
Definition: mythwebsocket.h:75
MythWebSocket::SendClose
void SendClose(WSErrorCode Code, const QString &Message={})
Definition: mythwebsocket.cpp:578
WSCloseNormal
@ WSCloseNormal
Definition: mythwebsockettypes.h:35
WSCloseGoingAway
@ WSCloseGoingAway
Definition: mythwebsockettypes.h:36
DataPayloads
std::vector< DataPayload > DataPayloads
Definition: mythhttpcommon.h:37