26 #include <QTextStream> 27 #include <QJsonDocument> 28 #include <QCryptographicHash> 68 m_socketDescriptor(SocketDescriptor),
72 m_authenticated(false),
73 m_challengeResponse(),
74 m_address(QHostAddress()),
79 m_currentRequestID(1),
94 m_socketDescriptor(0),
97 m_wsReader(*this, Protocol, false),
98 m_authenticated(false),
99 m_challengeResponse(),
103 m_subProtocol(Protocol),
105 m_currentRequestID(1),
116 if (!m_currentRequests.isEmpty())
120 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"%1 outstanding RPC requests").arg(m_currentRequests.size()));
122 while (!m_currentRequests.isEmpty())
126 if (m_socketState == SocketState::Upgraded)
136 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Trying to send response to upgrade request but not server side"));
137 SetState(SocketState::ErroredSt);
141 if (m_socketState != SocketState::ConnectedTo)
143 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Trying to send response to upgrade request but not in connected state (HTTP)"));
144 SetState(SocketState::ErroredSt);
149 bool versionerror =
false;
169 error = QStringLiteral(
"Must be GET and HTTP/1.1");
180 if (valid && Request.
GetPath().isEmpty())
182 error = QStringLiteral(
"invalid Request-URI");
192 if (valid && !Request.
Headers().contains(QStringLiteral(
"Host")))
194 error = QStringLiteral(
"No Host header");
200 QUrl host(QStringLiteral(
"http://%1").arg(Request.
Headers().value(QStringLiteral(
"Host"))));
201 int localport = localPort();
203 if (valid && localport != 80 && localport != 443 && host.port() != localport)
205 error = QStringLiteral(
"Invalid Host port");
212 if (valid && host.host() != localAddress().toString())
214 error = QStringLiteral(
"Invalid Host");
223 if (valid && !Request.
Headers().contains(QStringLiteral(
"Upgrade")))
225 error = QStringLiteral(
"No Upgrade header");
229 if (valid && !Request.
Headers().value(QStringLiteral(
"Upgrade")).contains(QStringLiteral(
"websocket"), Qt::CaseInsensitive))
231 error = QStringLiteral(
"No 'websocket request");
240 if (valid && !Request.
Headers().contains(QStringLiteral(
"Connection")))
242 error = QStringLiteral(
"No Connection header");
246 if (valid && !Request.
Headers().value(QStringLiteral(
"Connection")).contains(QStringLiteral(
"Upgrade"), Qt::CaseInsensitive))
248 error = QStringLiteral(
"No Upgrade request");
265 if (valid && !Request.
Headers().contains(QStringLiteral(
"Sec-WebSocket-Key")))
267 error = QStringLiteral(
"No Sec-WebSocket-Key header");
271 if (valid && Request.
Headers().value(QStringLiteral(
"Sec-WebSocket-Key")).isEmpty())
273 error = QStringLiteral(
"Invalid Sec-WebSocket-Key");
297 if (valid && !Request.
Headers().contains(QStringLiteral(
"Sec-WebSocket-Version")))
299 error = QStringLiteral(
"No Sec-WebSocket-Version header");
303 int version = Request.
Headers().value(QStringLiteral(
"Sec-WebSocket-Version")).toInt();
307 error = QStringLiteral(
"Unsupported WebSocket version");
324 if (Request.
Headers().contains(QStringLiteral(
"Sec-WebSocket-Protocol")))
327 if (!protocols.isEmpty())
328 protocol = protocols.first();
333 LOG(VB_GENERAL, LOG_ERR, error);
336 Request.
SetResponseHeader(QStringLiteral(
"Sec-WebSocket-Version"), QStringLiteral(
"8,13"));
341 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"Received valid websocket Upgrade request"));
343 QString key = QStringLiteral(
"%1%2").arg(Request.
Headers().value(QStringLiteral(
"Sec-WebSocket-Key")), QStringLiteral(
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
344 QString nonce = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toBase64();
349 Request.
SetResponseHeader(QStringLiteral(
"Upgrade"), QStringLiteral(
"websocket"));
354 m_subProtocol = protocol;
359 SetState(SocketState::Upgraded);
362 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"%1 socket upgraded (%2)").arg(m_debug, m_authenticated ? QStringLiteral(
"Authenticated") : QStringLiteral(
"Unauthenticated")));
364 if (Request.
Headers().contains(QStringLiteral(
"Torc-UUID")))
367 m_watchdogTimer.stop();
369 data.insert(
TORC_UUID, Request.
Headers().value(QStringLiteral(
"Torc-UUID")));
370 data.insert(
TORC_NAME, Request.
Headers().value(QStringLiteral(
"Torc-Name")));
371 data.insert(
TORC_PORT, Request.
Headers().value(QStringLiteral(
"Torc-Port")));
376 if (Request.
Headers().contains(QStringLiteral(
"Torc-Secure")))
386 if (Request.
GetMethod().startsWith(QStringLiteral(
"echo"), Qt::CaseInsensitive))
389 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Enabling WebSocket echo for testing"));
399 proto.insert(QStringLiteral(
"description"), QStringLiteral(
"I can't remember how this differs from straight JSON-RPC:) The overall mechanism is very similar to WAMP."));
400 result.append(proto);
406 if (m_debug.isEmpty())
410 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"%1 encrypted").arg(m_debug));
419 if (!allowed.isEmpty())
420 ignoreSslErrors(allowed);
444 QList<QSslError> ignore;
445 ignore << QSslError::SelfSignedCertificate;
446 ignoreSslErrors(ignore);
449 if (m_serverSide && m_socketDescriptor)
451 if (setSocketDescriptor(m_socketDescriptor))
454 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"%1 socket connected (%2)").arg(m_debug, m_authenticated ? QStringLiteral(
"Authenticated") : QStringLiteral(
"Unauthenticated")));
455 SetState(SocketState::ConnectedTo);
459 startServerEncryption();
470 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Failed to set socket descriptor"));
473 else if (!m_serverSide)
475 SetState(SocketState::ConnectingTo);
479 connectToHostEncrypted(m_address.toString(), m_port);
485 connectToHost(m_address, m_port);
491 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to start Websocket"));
492 SetState(SocketState::ErroredSt);
499 if (service && senderSignalIndex() > -1)
510 if (m_subscribers.contains(Method))
512 QMap<QString,QObject*>::const_iterator it = m_subscribers.constFind(Method);
513 while (it != m_subscribers.constEnd() && it.key() == Method)
515 QMetaObject::invokeMethod(it.value(),
"ServiceNotification", Q_ARG(QString, Method));
541 int id = m_currentRequestID++;
542 while (m_currentRequests.contains(
id))
543 id = m_currentRequestID++;
546 m_currentRequests.insert(
id, Request);
549 m_requestTimers.insert(startTimer(10000, Qt::CoarseTimer),
id);
552 if (m_currentRequestID > 100000)
553 m_currentRequestID = 1;
561 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"No protocol specified for remote procedure call"));
577 int id = Request->
GetID();
578 if (m_currentRequests.contains(
id))
581 int timer = m_requestTimers.key(
id);
583 m_requestTimers.remove(timer);
586 m_currentRequests.remove(
id);
591 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Cannot cancel unknown RPC request"));
595 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"RPC request is still referenced after cancellation - potential leak"));
599 void TorcWebSocket::ReadHTTP(
void)
603 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Trying to read HTTP but not server side"));
604 SetState(SocketState::ErroredSt);
608 if (m_socketState != SocketState::ConnectedTo)
610 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Trying to read HTTP but not in connected state (raw HTTP)"));
611 SetState(SocketState::ErroredSt);
615 while (canReadLine())
618 if (!m_reader.
Read(
this))
620 SetState(SocketState::ErroredSt);
628 if (bytesAvailable() > 0)
629 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"%1 unread bytes from %2").arg(bytesAvailable()).arg(peerAddress().toString()));
637 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Received unexpected HTTP response"));
638 SetState(SocketState::ErroredSt);
642 bool upgrade = request.
Headers().contains(QStringLiteral(
"Upgrade"));
647 HandleUpgradeRequest(request);
654 localAddress().toString(), localPort(), request);
671 if (m_watchdogTimer.isActive())
672 m_watchdogTimer.start();
678 static const int maxUnchanged = 5000;
679 static const int unchangedSleep = 1000;
680 int unchangedCount = 0;
682 while ((m_socketState == SocketState::ConnectedTo || m_socketState == SocketState::Upgrading || m_socketState == SocketState::Upgraded) &&
685 qint64 available = bytesAvailable();
687 if (m_socketState == SocketState::ConnectedTo)
693 if (m_socketState == SocketState::Upgrading)
699 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Upgrading state on server side"));
700 SetState(SocketState::ErroredSt);
710 if (m_socketState == SocketState::Upgraded)
712 if (!m_wsReader.
Read())
715 SetState(SocketState::Disconnecting);
726 if (bytesAvailable() == available)
729 if (unchangedCount > maxUnchanged)
731 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Socket time out waiting for valid data - closing"));
732 SetState(SocketState::Disconnecting);
735 TorcQThread::usleep(unchangedSleep);
747 QList<QString>
remove;
749 QMap<QString,QObject*>::const_iterator it = m_subscribers.constBegin();
750 for ( ; it != m_subscribers.constEnd(); ++it)
751 if (it.value() == Object)
752 remove.append(it.key());
754 foreach (
const QString &service,
remove)
756 m_subscribers.remove(service, Object);
757 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Removed stale subscription to '%1'").arg(service));
763 if(state() == QAbstractSocket::ConnectedState)
764 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"%1 disconnecting").arg(m_debug));
766 disconnectFromHost();
769 if (state() == QAbstractSocket::ConnectedState && !waitForDisconnected(1000))
770 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"WebSocket not successfully disconnected before closing"));
777 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"%1 connection to remote host").arg(m_debug));
778 SetState(SocketState::Upgrading);
782 void TorcWebSocket::SendHandshake(
void)
786 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Trying to send handshake from server side"));
787 SetState(SocketState::ErroredSt);
791 if (m_socketState != SocketState::Upgrading)
793 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Trying to upgrade from incorrect state (client side)"));
794 SetState(SocketState::ErroredSt);
798 QScopedPointer<QByteArray> upgrade(
new QByteArray());
799 QTextStream stream(upgrade.data());
802 for (
int i = 0; i < 16; ++i)
803 nonce.append(qrand() % 0x100);
804 nonce = nonce.toBase64();
807 QString key = QStringLiteral(
"%1%2").arg(nonce.data(), QStringLiteral(
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
808 m_challengeResponse = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toBase64();
810 QHostAddress host(m_address);
813 stream <<
"GET / HTTP/1.1\r\n";
816 stream <<
"Upgrade: WebSocket\r\n";
817 stream <<
"Connection: Upgrade\r\n";
818 stream <<
"Sec-WebSocket-Version: 13\r\n";
819 stream <<
"Sec-WebSocket-Key: " << nonce.data() <<
"\r\n";
823 stream <<
"Torc-Port: " << QString::number(server.
port) <<
"\r\n";
829 stream <<
"Torc-Secure: yes\r\n";
833 if (write(upgrade->data(), upgrade->size()) != upgrade->size())
835 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Unexpected write error"));
836 SetState(SocketState::ErroredSt);
840 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"%1 client WebSocket connected (SubProtocol: %2)")
843 LOG(VB_NETWORK, LOG_DEBUG, QStringLiteral(
"Data...\r\n%1").arg(upgrade->data()));
846 void TorcWebSocket::ReadHandshake(
void)
850 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Trying to read handshake server side"));
851 SetState(SocketState::ErroredSt);
855 if (m_socketState != SocketState::Upgrading)
857 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Trying to read handshake but not upgrading"));
858 SetState(SocketState::ErroredSt);
863 if (!m_reader.
Read(
this))
865 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Error reading upgrade response"));
866 SetState(SocketState::ErroredSt);
884 error = QStringLiteral(
"Response is not an HTTP response");
896 if (valid && !(request.
Headers().contains(QStringLiteral(
"Upgrade")) && request.
Headers().contains(QStringLiteral(
"Connection")) &&
897 request.
Headers().contains(QStringLiteral(
"Sec-WebSocket-Accept"))))
900 error = QStringLiteral(
"Response is missing required headers");
906 QString upgrade = request.
Headers().value(QStringLiteral(
"Upgrade")).trimmed();
907 QString connection = request.
Headers().value(QStringLiteral(
"Connection")).trimmed();
908 QString accept = request.
Headers().value(QStringLiteral(
"Sec-WebSocket-Accept")).trimmed();
909 QString protocols = request.
Headers().value(QStringLiteral(
"Sec-WebSocket-Protocol")).trimmed();
911 if (!upgrade.contains(QStringLiteral(
"websocket"), Qt::CaseInsensitive) || !connection.contains(QStringLiteral(
"upgrade"), Qt::CaseInsensitive))
914 error = QStringLiteral(
"Unexpected header content");
918 if (!accept.contains(m_challengeResponse, Qt::CaseSensitive))
921 error = QStringLiteral(
"Incorrect Sec-WebSocket-Accept response");
930 if (!protocols.isEmpty())
933 error = QStringLiteral(
"Unexpected subprotocol");
939 if ((subprotocols | m_subProtocol) != m_subProtocol)
942 error = QStringLiteral(
"Unexpected subprotocol");
950 LOG(VB_GENERAL, LOG_ERR, error);
951 SetState(SocketState::ErroredSt);
955 LOG(VB_GENERAL, LOG_DEBUG, QStringLiteral(
"Received valid upgrade response - switching to frame protocol"));
956 SetState(SocketState::Upgraded);
961 if (QAbstractSocket::RemoteHostClosedError == SocketError)
962 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"%1 remote host disconnected socket").arg(m_debug));
964 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"%1 socket error %2 '%3'").arg(m_debug).arg(SocketError).arg(errorString()));
965 SetState(SocketState::ErroredSt);
970 if(m_socketState == SocketState::Upgraded)
971 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"%1 no websocket traffic for %2seconds").arg(m_debug).arg(m_watchdogTimer.interval() / 1000));
973 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"%1 no HTTP traffic for %2seconds").arg(m_debug).arg(m_watchdogTimer.interval() / 1000));
974 SetState(SocketState::DisconnectedSt);
979 if (m_watchdogTimer.isActive())
980 m_watchdogTimer.start();
985 if (Event->type() == QEvent::Timer)
987 QTimerEvent *
event =
static_cast<QTimerEvent*
>(Event);
990 int timerid =
event->timerId();
992 if (m_requestTimers.contains(timerid))
995 int requestid = m_requestTimers.value(timerid);
997 m_requestTimers.remove(timerid);
1001 if (m_currentRequests.contains(requestid) && (request = m_currentRequests.value(requestid)))
1004 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"'%1' request timed out").arg(request->
GetMethod()));
1008 m_currentRequests.remove(requestid);
1017 return QSslSocket::event(Event);
1022 if (State == m_socketState)
1025 m_socketState = State;
1027 if (m_socketState == SocketState::Disconnecting || m_socketState == SocketState::DisconnectedSt ||
1028 m_socketState == SocketState::ErroredSt)
1033 if (m_socketState == SocketState::Upgraded)
1037 void TorcWebSocket::ProcessPayload(
const QByteArray &Payload)
1047 if (!request->
GetData().isEmpty())
1052 else if (request->
GetID() > -1)
1054 int id = request->
GetID();
1056 if (m_currentRequests.contains(
id) && (requestor = m_currentRequests.value(
id)))
1066 QString method = requestor->
GetMethod();
1068 if (method.endsWith(QStringLiteral(
"/Subscribe")))
1071 QObject *parent = requestor->
GetParent();
1073 if (parent->metaObject()->indexOfSlot(QMetaObject::normalizedSignature(
"ServiceNotification(QString)")) < 0)
1075 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Cannot monitor subscription to '%1' for object '%2' - no notification slot").arg(method, parent->objectName()));
1077 else if (request->
GetReply().type() == QVariant::Map)
1082 QVariantMap map = request->
GetReply().toMap();
1083 if (map.contains(QStringLiteral(
"properties")) && map.value(QStringLiteral(
"properties")).type() == QVariant::List)
1085 QVariantList properties = map.value(QStringLiteral(
"properties")).toList();
1088 QVariantList::const_iterator it = properties.constBegin();
1089 for ( ; it != properties.constEnd(); ++it)
1091 if (it->type() == QVariant::Map)
1093 QVariantMap
property = it->toMap();
1094 if (property.contains(QStringLiteral(
"notification")))
1096 QString service = method +
property.value(QStringLiteral(
"notification")).toString();
1097 if (m_subscribers.contains(service, parent))
1099 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Object '%1' already has subscription to '%2'").arg(parent->objectName(), service));
1103 m_subscribers.insertMulti(service, parent);
1104 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Object '%1' subscribed to '%2'").arg(parent->objectName(), service));
1113 else if (method.endsWith(QStringLiteral(
"/Unsubscribe")))
1116 QObject *parent = requestor->
GetParent();
1121 QMap<QString,QObject*>::const_iterator it = m_subscribers.constBegin();
1122 for ( ; it != m_subscribers.constEnd(); ++it)
1123 if (it.value() == parent && it.key().startsWith(method))
1124 remove.append(it.key());
1126 foreach (
const QString &signature,
remove)
1128 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Object '%1' unsubscribed from '%2'").arg(parent->objectName(), signature));
1129 m_subscribers.remove(signature, parent);
1133 if (std::find(m_subscribers.cbegin(), m_subscribers.cend(), parent) == m_subscribers.cend())
1136 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"'%1' disconnect - no more subscriptions").arg(parent->objectName()));
1137 (void)disconnect(parent,
nullptr,
this,
nullptr);
1145 m_currentRequests.remove(
id);
void BytesWritten(qint64)
void SetSubProtocol(WSSubProtocol Protocol)
static QList< WSSubProtocol > SubProtocolsFromPrioritisedString(const QString &Protocols)
Parse a prioritised list of supported WebSocket sub-protocols.
A class to encapsulate an incoming HTTP request.
HTTPRequestType GetHTTPRequestType(void) const
void SubscriberDeleted(QObject *Subscriber)
A subscriber object has been deleted.
const QByteArray & GetPayload(void)
bool Read(QTcpSocket *Socket)
Read and parse data from the given socket.
Overlays the Websocket protocol over a QTcpSocket.
TorcLocalContext * gLocalContext
void SetConnection(HTTPConnection Connection)
static void Authorise(const QString &Host, TorcHTTPRequest &Request, bool ForceCheck)
Ensures remote user is authorised to access this request.
void ConnectionUpgraded(void)
QString Signature(void) const
HTTPStatus GetHTTPStatus(void) const
static QString SubProtocolsToString(WSSubProtocols Protocols)
Convert SubProtocols to HTTP readable string.
QByteArray & GetData(void)
static QString StatusToString(HTTPStatus Status)
bool HandleNotification(const QString &Method)
static void HandleRequest(const QString &PeerAddress, int PeerPort, const QString &LocalAddress, int LocalPort, TorcHTTPRequest &Request)
QString GetUuid(void) const
static QString IPAddressToLiteral(const QHostAddress &Address, int Port, bool UseLocalhost=true)
Convert an IP address to a string literal.
void CancelRequest(TorcRPCRequest *Request)
Cancel a Remote Procedure Call.
virtual bool DownRef(void)
HTTPAuthorisation IsAuthorised(void) const
void AddState(int State)
Progress the state for this request.
void SendFrame(OpCode Code, QByteArray &Payload)
A class encapsulating a Remote Procedure Call.
void NotifyParent(void)
Signal to the parent that the request is ready (but may be errored).
static QList< QSslError > AllowableSslErrors(const QList< QSslError > &Errors)
static void PeerConnected(TorcWebSocketThread *Thread, const QVariantMap &Data)
Respond to a valid WebSocket upgrade request and schedule creation of a WebSocket on the give QTcpSoc...
QString GetMethod(void) const
TorcWebSocket(TorcWebSocketThread *Parent, qintptr SocketDescriptor, bool Secure)
void SSLErrors(const QList< QSslError > &Errors)
QString GetPath(void) const
const QMap< QString, QString > & Headers(void) const
QString GetMethod(void) const
static Status GetStatus(void)
#define FULL_SOCKET_TIMEOUT
bool event(QEvent *Event) override
#define HTTP_SOCKET_TIMEOUT
bool IsNotification(void) const
void SetResponseHeader(const QString &Header, const QString &Value)
QByteArray & SerialiseRequest(TorcWebSocketReader::WSSubProtocol Protocol)
Serialise the request for the given protocol.
static WSSubProtocols SubProtocolsFromString(const QString &Protocols)
Parse supported WSSubProtocols from Protocols.
const QVariant & GetReply(void) const
static QVariantList GetSupportedSubProtocols(void)
Return a list of supported WebSocket sub-protocols.
static QString GetVersion(void)
void SetSecure(bool Secure)
void SetStatus(HTTPStatus Status)
#define LOG(_MASK_, _LEVEL_, _STRING_)
static QString PlatformName(void)
QVariant GetProperty(int Index)
Get the value of a given property.
QString GetMethod(int Index)
void InitiateClose(CloseCode Close, const QString &Reason)
void ReadyRead(void)
Process incoming data.
void SetReply(const QVariant &Reply)
void SetResponseType(HTTPResponseType Type)
Wraps a TorcQThread around a TorcWebsocket.
void Error(QAbstractSocket::SocketError)
qint64 GetStartTime(void)
HTTPProtocol GetHTTPProtocol(void) const
static QString ServerDescription(void)
static OpCode FormatForSubProtocol(WSSubProtocol Protocol)
HTTPType GetHTTPType(void) const
QObject * GetParent(void) const
void PropertyChanged(void)
Receives notifications when a property for a subscribed service has changed.
void RemoteRequest(TorcRPCRequest *Request)
Initiate a Remote Procedure Call.
void Start(void)
Initialise the websocket once its parent thread is ready.
void AddParameter(const QString &Name, const QVariant &Value)
Add Parameter and value to list of call parameters.
void SetID(int ID)
Set the unique ID for this request.
void ConnectionEstablished(void)
void Reset(void)
Reset the read state.
void Respond(QTcpSocket *Socket)