summaryrefslogtreecommitdiff
path: root/src/websockets
diff options
context:
space:
mode:
Diffstat (limited to 'src/websockets')
-rw-r--r--src/websockets/doc/qtwebsockets.qdocconf1
-rw-r--r--src/websockets/qwebsocket_p.cpp8
-rw-r--r--src/websockets/qwebsocket_p.h4
-rw-r--r--src/websockets/qwebsocket_wasm_p.cpp16
-rw-r--r--src/websockets/qwebsocketdataprocessor.cpp39
-rw-r--r--src/websockets/qwebsocketdataprocessor_p.h7
-rw-r--r--src/websockets/qwebsocketframe.cpp370
-rw-r--r--src/websockets/qwebsocketframe_p.h13
-rw-r--r--src/websockets/qwebsockethandshakeresponse.cpp9
-rw-r--r--src/websockets/qwebsocketserver.cpp24
-rw-r--r--src/websockets/qwebsocketserver.h16
11 files changed, 295 insertions, 212 deletions
diff --git a/src/websockets/doc/qtwebsockets.qdocconf b/src/websockets/doc/qtwebsockets.qdocconf
index ba77c82..56d54a2 100644
--- a/src/websockets/doc/qtwebsockets.qdocconf
+++ b/src/websockets/doc/qtwebsockets.qdocconf
@@ -1,4 +1,5 @@
include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+include($QT_INSTALL_DOCS/config/exampleurl-qtwebsockets.qdocconf)
project = QtWebSockets
description = Qt WebSockets Reference Documentation
diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp
index 6965731..36aefd9 100644
--- a/src/websockets/qwebsocket_p.cpp
+++ b/src/websockets/qwebsocket_p.cpp
@@ -113,6 +113,7 @@ QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::
m_defaultMaskGenerator(),
m_handshakeState(NothingDoneState)
{
+ m_pingTimer.start();
}
/*!
@@ -144,6 +145,7 @@ QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol:
m_defaultMaskGenerator(),
m_handshakeState(NothingDoneState)
{
+ m_pingTimer.start();
}
/*!
@@ -1176,10 +1178,10 @@ void QWebSocketPrivate::processData()
while (m_pSocket->bytesAvailable()) {
if (state() == QAbstractSocket::ConnectingState) {
if (!m_pSocket->canReadLine())
- break;
+ return;
processHandshake(m_pSocket);
- } else {
- m_dataProcessor.process(m_pSocket);
+ } else if (!m_dataProcessor.process(m_pSocket)) {
+ return;
}
}
}
diff --git a/src/websockets/qwebsocket_p.h b/src/websockets/qwebsocket_p.h
index 4b39dfc..e72daa5 100644
--- a/src/websockets/qwebsocket_p.h
+++ b/src/websockets/qwebsocket_p.h
@@ -61,7 +61,7 @@
#include <QtNetwork/QSslError>
#include <QtNetwork/QSslSocket>
#endif
-#include <QtCore/QTime>
+#include <QtCore/QElapsedTimer>
#include <private/qobject_p.h>
#include "qwebsocket.h"
@@ -229,7 +229,7 @@ private:
QWebSocketProtocol::CloseCode m_closeCode;
QString m_closeReason;
- QTime m_pingTimer;
+ QElapsedTimer m_pingTimer;
QWebSocketDataProcessor m_dataProcessor;
QWebSocketConfiguration m_configuration;
diff --git a/src/websockets/qwebsocket_wasm_p.cpp b/src/websockets/qwebsocket_wasm_p.cpp
index 199fe44..85fcab2 100644
--- a/src/websockets/qwebsocket_wasm_p.cpp
+++ b/src/websockets/qwebsocket_wasm_p.cpp
@@ -137,11 +137,19 @@ qint64 QWebSocketPrivate::sendTextMessage(const QString &message)
qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data)
{
- socketContext.call<void>("send",
- val(typed_memory_view(data.size(),
- reinterpret_cast<const unsigned char *>
- (data.constData()))));
+ // Make a copy of the payload data; we don't know how long WebSocket.send() will
+ // retain the memory view, while the QByteArray passed to this function may be
+ // destroyed as soon as this function returns. In addition, the WebSocket.send()
+ // API does not accept data from a view backet by a SharedArrayBuffer, which will
+ // be the case for the view produced by typed_memory_view() when threads are enabled.
+ val Uint8Array = val::global("Uint8Array");
+ val dataCopy = Uint8Array.new_(data.size());
+ val dataView = val(typed_memory_view(data.size(),
+ reinterpret_cast<const unsigned char *>
+ (data.constData())));
+ dataCopy.call<void>("set", dataView);
+ socketContext.call<void>("send", dataCopy);
return data.length();
}
diff --git a/src/websockets/qwebsocketdataprocessor.cpp b/src/websockets/qwebsocketdataprocessor.cpp
index 4f81222..191b992 100644
--- a/src/websockets/qwebsocketdataprocessor.cpp
+++ b/src/websockets/qwebsocketdataprocessor.cpp
@@ -87,6 +87,10 @@ QWebSocketDataProcessor::QWebSocketDataProcessor(QObject *parent) :
m_pTextCodec(QTextCodec::codecForName("UTF-8"))
{
clear();
+ // initialize the internal timeout timer
+ waitTimer.setInterval(5000);
+ waitTimer.setSingleShot(true);
+ waitTimer.callOnTimeout(this, &QWebSocketDataProcessor::timeout);
}
/*!
@@ -119,14 +123,23 @@ quint64 QWebSocketDataProcessor::maxFrameSize()
/*!
\internal
+
+ Returns \c true if a complete websocket frame has been processed;
+ otherwise returns \c false.
*/
-void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
+bool QWebSocketDataProcessor::process(QIODevice *pIoDevice)
{
bool isDone = false;
while (!isDone) {
- QWebSocketFrame frame = QWebSocketFrame::readFrame(pIoDevice);
- if (Q_LIKELY(frame.isValid())) {
+ frame.readFrame(pIoDevice);
+ if (!frame.isDone()) {
+ // waiting for more data available
+ QObject::connect(pIoDevice, &QIODevice::readyRead,
+ &waitTimer, &QTimer::stop, Qt::UniqueConnection);
+ waitTimer.start();
+ return false;
+ } else if (Q_LIKELY(frame.isValid())) {
if (frame.isControlFrame()) {
isDone = processControlFrame(frame);
} else {
@@ -136,7 +149,7 @@ void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeProtocolError,
tr("Received Continuation frame, while there is " \
"nothing to continue."));
- return;
+ return true;
}
if (Q_UNLIKELY(m_isFragmented && frame.isDataFrame() &&
!frame.isContinuationFrame())) {
@@ -144,7 +157,7 @@ void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeProtocolError,
tr("All data frames after the initial data frame " \
"must have opcode 0 (continuation)."));
- return;
+ return true;
}
if (!frame.isContinuationFrame()) {
m_opCode = frame.opCode();
@@ -158,7 +171,7 @@ void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
clear();
Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeTooMuchData,
tr("Received message is too big."));
- return;
+ return true;
}
if (m_opCode == QWebSocketProtocol::OpCodeText) {
@@ -171,7 +184,7 @@ void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
clear();
Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeWrongDatatype,
tr("Invalid UTF-8 code encountered."));
- return;
+ return true;
} else {
m_textMessage.append(frameTxt);
Q_EMIT textFrameReceived(frameTxt, frame.isFinalFrame());
@@ -199,7 +212,9 @@ void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
clear();
isDone = true;
}
+ frame.clear();
}
+ return true;
}
/*!
@@ -301,4 +316,14 @@ bool QWebSocketDataProcessor::processControlFrame(const QWebSocketFrame &frame)
return mustStopProcessing;
}
+/*!
+ \internal
+ */
+void QWebSocketDataProcessor::timeout()
+{
+ clear();
+ Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeGoingAway,
+ tr("Timeout when reading data from socket."));
+}
+
QT_END_NAMESPACE
diff --git a/src/websockets/qwebsocketdataprocessor_p.h b/src/websockets/qwebsocketdataprocessor_p.h
index e80a843..03635b1 100644
--- a/src/websockets/qwebsocketdataprocessor_p.h
+++ b/src/websockets/qwebsocketdataprocessor_p.h
@@ -55,6 +55,8 @@
#include <QtCore/QByteArray>
#include <QtCore/QString>
#include <QtCore/QTextCodec>
+#include <QTimer>
+#include "qwebsocketframe_p.h"
#include "qwebsocketprotocol.h"
#include "qwebsocketprotocol_p.h"
@@ -86,7 +88,7 @@ Q_SIGNALS:
void errorEncountered(QWebSocketProtocol::CloseCode code, const QString &description);
public Q_SLOTS:
- void process(QIODevice *pIoDevice);
+ bool process(QIODevice *pIoDevice);
void clear();
private:
@@ -111,8 +113,11 @@ private:
quint64 m_payloadLength;
QTextCodec::ConverterState *m_pConverterState;
QTextCodec *m_pTextCodec;
+ QWebSocketFrame frame;
+ QTimer waitTimer;
bool processControlFrame(const QWebSocketFrame &frame);
+ void timeout();
};
QT_END_NAMESPACE
diff --git a/src/websockets/qwebsocketframe.cpp b/src/websockets/qwebsocketframe.cpp
index 041302e..11373a7 100644
--- a/src/websockets/qwebsocketframe.cpp
+++ b/src/websockets/qwebsocketframe.cpp
@@ -93,7 +93,8 @@ QWebSocketFrame::QWebSocketFrame(const QWebSocketFrame &other) :
m_rsv1(other.m_rsv1),
m_rsv2(other.m_rsv2),
m_rsv3(other.m_rsv3),
- m_isValid(other.m_isValid)
+ m_isValid(other.m_isValid),
+ m_processingState(other.m_processingState)
{
}
@@ -113,6 +114,7 @@ QWebSocketFrame &QWebSocketFrame::operator =(const QWebSocketFrame &other)
m_length = other.m_length;
m_payload = other.m_payload;
m_isValid = other.m_isValid;
+ m_processingState = other.m_processingState;
return *this;
}
@@ -132,7 +134,8 @@ QWebSocketFrame::QWebSocketFrame(QWebSocketFrame &&other) :
m_rsv1(qMove(other.m_rsv1)),
m_rsv2(qMove(other.m_rsv2)),
m_rsv3(qMove(other.m_rsv3)),
- m_isValid(qMove(other.m_isValid))
+ m_isValid(qMove(other.m_isValid)),
+ m_processingState(qMove(other.m_processingState))
{}
@@ -152,6 +155,7 @@ QWebSocketFrame &QWebSocketFrame::operator =(QWebSocketFrame &&other)
qSwap(m_length, other.m_length);
qSwap(m_payload, other.m_payload);
qSwap(m_isValid, other.m_isValid);
+ qSwap(m_processingState, other.m_processingState);
return *this;
}
@@ -175,6 +179,7 @@ void QWebSocketFrame::swap(QWebSocketFrame &other)
qSwap(m_length, other.m_length);
qSwap(m_payload, other.m_payload);
qSwap(m_isValid, other.m_isValid);
+ qSwap(m_processingState, other.m_processingState);
}
}
@@ -183,7 +188,7 @@ void QWebSocketFrame::swap(QWebSocketFrame &other)
*/
QWebSocketProtocol::CloseCode QWebSocketFrame::closeCode() const
{
- return m_closeCode;
+ return isDone() ? m_closeCode : QWebSocketProtocol::CloseCodeGoingAway;
}
/*!
@@ -191,7 +196,7 @@ QWebSocketProtocol::CloseCode QWebSocketFrame::closeCode() const
*/
QString QWebSocketFrame::closeReason() const
{
- return m_closeReason;
+ return isDone() ? m_closeReason : tr("Waiting for more data from socket.");
}
/*!
@@ -276,6 +281,7 @@ void QWebSocketFrame::clear()
m_length = 0;
m_payload.clear();
m_isValid = false;
+ m_processingState = PS_READ_HEADER;
}
/*!
@@ -283,215 +289,211 @@ void QWebSocketFrame::clear()
*/
bool QWebSocketFrame::isValid() const
{
- return m_isValid;
+ return isDone() && m_isValid;
}
-#define WAIT_FOR_MORE_DATA(dataSizeInBytes) \
- { returnState = processingState; \
- processingState = PS_WAIT_FOR_MORE_DATA; dataWaitSize = dataSizeInBytes; }
+/*!
+ \internal
+ */
+bool QWebSocketFrame::isDone() const
+{
+ return m_processingState == PS_DISPATCH_RESULT;
+}
/*!
\internal
*/
-QWebSocketFrame QWebSocketFrame::readFrame(QIODevice *pIoDevice)
+void QWebSocketFrame::readFrame(QIODevice *pIoDevice)
{
- bool isDone = false;
- qint64 bytesRead = 0;
- QWebSocketFrame frame;
- quint64 dataWaitSize = 0;
- Q_UNUSED(dataWaitSize); // value is used in MACRO, Q_UNUSED to avoid compiler warnings
- ProcessingState processingState = PS_READ_HEADER;
- ProcessingState returnState = PS_READ_HEADER;
- bool hasMask = false;
- quint64 payloadLength = 0;
-
- while (!isDone)
+ while (true)
{
- switch (processingState) {
- case PS_WAIT_FOR_MORE_DATA:
- //TODO: waitForReadyRead should really be changed
- //now, when a WebSocket is used in a GUI thread
- //the GUI will hang for at most 5 seconds
- //maybe, a QStateMachine should be used
- if (!pIoDevice->waitForReadyRead(5000)) {
- frame.setError(QWebSocketProtocol::CloseCodeGoingAway,
- tr("Timeout when reading data from socket."));
- processingState = PS_DISPATCH_RESULT;
- } else {
- processingState = returnState;
- }
- break;
-
+ switch (m_processingState) {
case PS_READ_HEADER:
- if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
- //FIN, RSV1-3, Opcode
- char header[2] = {0};
- bytesRead = pIoDevice->read(header, 2);
- frame.m_isFinalFrame = (header[0] & 0x80) != 0;
- frame.m_rsv1 = (header[0] & 0x40);
- frame.m_rsv2 = (header[0] & 0x20);
- frame.m_rsv3 = (header[0] & 0x10);
- frame.m_opCode = static_cast<QWebSocketProtocol::OpCode>(header[0] & 0x0F);
-
- //Mask, PayloadLength
- hasMask = (header[1] & 0x80) != 0;
- frame.m_length = (header[1] & 0x7F);
-
- switch (frame.m_length)
- {
- case 126:
- {
- processingState = PS_READ_PAYLOAD_LENGTH;
- break;
- }
- case 127:
- {
- processingState = PS_READ_BIG_PAYLOAD_LENGTH;
- break;
- }
- default:
- {
- payloadLength = frame.m_length;
- processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
- break;
- }
- }
- if (!frame.checkValidity())
- processingState = PS_DISPATCH_RESULT;
- } else {
- WAIT_FOR_MORE_DATA(2);
+ m_processingState = readFrameHeader(pIoDevice);
+ if (m_processingState == PS_WAIT_FOR_MORE_DATA) {
+ m_processingState = PS_READ_HEADER;
+ return;
}
break;
case PS_READ_PAYLOAD_LENGTH:
- if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
- uchar length[2] = {0};
- bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 2);
- if (Q_UNLIKELY(bytesRead == -1)) {
- frame.setError(QWebSocketProtocol::CloseCodeGoingAway,
- tr("Error occurred while reading from the network: %1")
- .arg(pIoDevice->errorString()));
- processingState = PS_DISPATCH_RESULT;
- } else {
- payloadLength = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(length));
- if (Q_UNLIKELY(payloadLength < 126)) {
- //see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
- //"in all cases, the minimal number of bytes MUST be used to encode
- //the length, for example, the length of a 124-byte-long string
- //can't be encoded as the sequence 126, 0, 124"
- frame.setError(QWebSocketProtocol::CloseCodeProtocolError,
- tr("Lengths smaller than 126 " \
- "must be expressed as one byte."));
- processingState = PS_DISPATCH_RESULT;
- } else {
- processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
- }
- }
- } else {
- WAIT_FOR_MORE_DATA(2);
+ m_processingState = readFramePayloadLength(pIoDevice);
+ if (m_processingState == PS_WAIT_FOR_MORE_DATA) {
+ m_processingState = PS_READ_PAYLOAD_LENGTH;
+ return;
}
break;
- case PS_READ_BIG_PAYLOAD_LENGTH:
- if (Q_LIKELY(pIoDevice->bytesAvailable() >= 8)) {
- uchar length[8] = {0};
- bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 8);
- if (Q_UNLIKELY(bytesRead < 8)) {
- frame.setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
- tr("Something went wrong during "\
- "reading from the network."));
- processingState = PS_DISPATCH_RESULT;
- } else {
- //Most significant bit must be set to 0 as
- //per http://tools.ietf.org/html/rfc6455#section-5.2
- payloadLength = qFromBigEndian<quint64>(length);
- if (Q_UNLIKELY(payloadLength & (quint64(1) << 63))) {
- frame.setError(QWebSocketProtocol::CloseCodeProtocolError,
- tr("Highest bit of payload length is not 0."));
- processingState = PS_DISPATCH_RESULT;
- } else if (Q_UNLIKELY(payloadLength <= 0xFFFFu)) {
- //see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
- //"in all cases, the minimal number of bytes MUST be used to encode
- //the length, for example, the length of a 124-byte-long string
- //can't be encoded as the sequence 126, 0, 124"
- frame.setError(QWebSocketProtocol::CloseCodeProtocolError,
- tr("Lengths smaller than 65536 (2^16) " \
- "must be expressed as 2 bytes."));
- processingState = PS_DISPATCH_RESULT;
- } else {
- processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
- }
- }
- } else {
- WAIT_FOR_MORE_DATA(8);
- }
-
- break;
-
case PS_READ_MASK:
- if (Q_LIKELY(pIoDevice->bytesAvailable() >= 4)) {
- bytesRead = pIoDevice->read(reinterpret_cast<char *>(&frame.m_mask),
- sizeof(frame.m_mask));
- if (bytesRead == -1) {
- frame.setError(QWebSocketProtocol::CloseCodeGoingAway,
- tr("Error while reading from the network: %1.")
- .arg(pIoDevice->errorString()));
- processingState = PS_DISPATCH_RESULT;
- } else {
- frame.m_mask = qFromBigEndian(frame.m_mask);
- processingState = PS_READ_PAYLOAD;
- }
- } else {
- WAIT_FOR_MORE_DATA(4);
+ m_processingState = readFrameMask(pIoDevice);
+ if (m_processingState == PS_WAIT_FOR_MORE_DATA) {
+ m_processingState = PS_READ_MASK;
+ return;
}
break;
case PS_READ_PAYLOAD:
- if (!payloadLength) {
- processingState = PS_DISPATCH_RESULT;
- } else if (Q_UNLIKELY(payloadLength > MAX_FRAME_SIZE_IN_BYTES)) {
- frame.setError(QWebSocketProtocol::CloseCodeTooMuchData,
- tr("Maximum framesize exceeded."));
- processingState = PS_DISPATCH_RESULT;
- } else {
- quint64 bytesAvailable = quint64(pIoDevice->bytesAvailable());
- if (bytesAvailable >= payloadLength) {
- frame.m_payload = pIoDevice->read(int(payloadLength));
- //payloadLength can be safely cast to an integer,
- //because MAX_FRAME_SIZE_IN_BYTES = MAX_INT
- if (Q_UNLIKELY(frame.m_payload.length() != int(payloadLength))) {
- //some error occurred; refer to the Qt documentation of QIODevice::read()
- frame.setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
- tr("Some serious error occurred " \
- "while reading from the network."));
- processingState = PS_DISPATCH_RESULT;
- } else {
- if (hasMask)
- QWebSocketProtocol::mask(&frame.m_payload, frame.m_mask);
- processingState = PS_DISPATCH_RESULT;
- }
- } else {
- //if payload is too big, then this will timeout
- WAIT_FOR_MORE_DATA(payloadLength);
- }
+ m_processingState = readFramePayload(pIoDevice);
+ if (m_processingState == PS_WAIT_FOR_MORE_DATA) {
+ m_processingState = PS_READ_PAYLOAD;
+ return;
}
break;
case PS_DISPATCH_RESULT:
- processingState = PS_READ_HEADER;
- isDone = true;
- break;
+ return;
default:
- //should not come here
- qWarning() << "DataProcessor::process: Found invalid state. This should not happen!";
- frame.clear();
- isDone = true;
- break;
- } //end switch
+ Q_UNREACHABLE();
+ return;
+ }
+ }
+}
+
+/*!
+ \internal
+ */
+QWebSocketFrame::ProcessingState QWebSocketFrame::readFrameHeader(QIODevice *pIoDevice)
+{
+ if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
+ // FIN, RSV1-3, Opcode
+ char header[2] = {0};
+ if (Q_UNLIKELY(pIoDevice->read(header, 2) < 2)) {
+ setError(QWebSocketProtocol::CloseCodeGoingAway,
+ tr("Error occurred while reading header from the network: %1")
+ .arg(pIoDevice->errorString()));
+ return PS_DISPATCH_RESULT;
+ }
+ m_isFinalFrame = (header[0] & 0x80) != 0;
+ m_rsv1 = (header[0] & 0x40);
+ m_rsv2 = (header[0] & 0x20);
+ m_rsv3 = (header[0] & 0x10);
+ m_opCode = static_cast<QWebSocketProtocol::OpCode>(header[0] & 0x0F);
+
+ // Mask
+ // Use zero as mask value to mean there's no mask to read.
+ // When the mask value is read, it over-writes this non-zero value.
+ m_mask = header[1] & 0x80;
+ // PayloadLength
+ m_length = (header[1] & 0x7F);
+
+ if (!checkValidity())
+ return PS_DISPATCH_RESULT;
+
+ switch (m_length) {
+ case 126:
+ case 127:
+ return PS_READ_PAYLOAD_LENGTH;
+ default:
+ return hasMask() ? PS_READ_MASK : PS_READ_PAYLOAD;
+ }
+ }
+ return PS_WAIT_FOR_MORE_DATA;
+}
+
+/*!
+ \internal
+ */
+QWebSocketFrame::ProcessingState QWebSocketFrame::readFramePayloadLength(QIODevice *pIoDevice)
+{
+ // see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
+ // in all cases, the minimal number of bytes MUST be used to encode the length,
+ // for example, the length of a 124-byte-long string can't be encoded as the
+ // sequence 126, 0, 124"
+ switch (m_length) {
+ case 126:
+ if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
+ uchar length[2] = {0};
+ if (Q_UNLIKELY(pIoDevice->read(reinterpret_cast<char *>(length), 2) < 2)) {
+ setError(QWebSocketProtocol::CloseCodeGoingAway,
+ tr("Error occurred while reading from the network: %1")
+ .arg(pIoDevice->errorString()));
+ return PS_DISPATCH_RESULT;
+ }
+ m_length = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(length));
+ if (Q_UNLIKELY(m_length < 126)) {
+
+ setError(QWebSocketProtocol::CloseCodeProtocolError,
+ tr("Lengths smaller than 126 must be expressed as one byte."));
+ return PS_DISPATCH_RESULT;
+ }
+ return hasMask() ? PS_READ_MASK : PS_READ_PAYLOAD;
+ }
+ break;
+ case 127:
+ if (Q_LIKELY(pIoDevice->bytesAvailable() >= 8)) {
+ uchar length[8] = {0};
+ if (Q_UNLIKELY(pIoDevice->read(reinterpret_cast<char *>(length), 8) < 8)) {
+ setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
+ tr("Something went wrong during reading from the network."));
+ return PS_DISPATCH_RESULT;
+ }
+ // Most significant bit must be set to 0 as
+ // per http://tools.ietf.org/html/rfc6455#section-5.2
+ m_length = qFromBigEndian<quint64>(length);
+ if (Q_UNLIKELY(m_length & (quint64(1) << 63))) {
+ setError(QWebSocketProtocol::CloseCodeProtocolError,
+ tr("Highest bit of payload length is not 0."));
+ return PS_DISPATCH_RESULT;
+ }
+ if (Q_UNLIKELY(m_length <= 0xFFFFu)) {
+ setError(QWebSocketProtocol::CloseCodeProtocolError,
+ tr("Lengths smaller than 65536 (2^16) must be expressed as 2 bytes."));
+ return PS_DISPATCH_RESULT;
+ }
+ return hasMask() ? PS_READ_MASK : PS_READ_PAYLOAD;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
}
+ return PS_WAIT_FOR_MORE_DATA;
+}
- return frame;
+/*!
+ \internal
+ */
+QWebSocketFrame::ProcessingState QWebSocketFrame::readFrameMask(QIODevice *pIoDevice)
+{
+ if (Q_LIKELY(pIoDevice->bytesAvailable() >= 4)) {
+ if (Q_UNLIKELY(pIoDevice->read(reinterpret_cast<char *>(&m_mask), sizeof(m_mask)) < 4)) {
+ setError(QWebSocketProtocol::CloseCodeGoingAway,
+ tr("Error while reading from the network: %1.").arg(pIoDevice->errorString()));
+ return PS_DISPATCH_RESULT;
+ }
+ m_mask = qFromBigEndian(m_mask);
+ return PS_READ_PAYLOAD;
+ }
+ return PS_WAIT_FOR_MORE_DATA;
+}
+
+/*!
+ \internal
+ */
+QWebSocketFrame::ProcessingState QWebSocketFrame::readFramePayload(QIODevice *pIoDevice)
+{
+ if (!m_length)
+ return PS_DISPATCH_RESULT;
+
+ if (Q_UNLIKELY(m_length > MAX_FRAME_SIZE_IN_BYTES)) {
+ setError(QWebSocketProtocol::CloseCodeTooMuchData, tr("Maximum framesize exceeded."));
+ return PS_DISPATCH_RESULT;
+ }
+ if (quint64(pIoDevice->bytesAvailable()) >= m_length) {
+ m_payload = pIoDevice->read(int(m_length));
+ // m_length can be safely cast to an integer,
+ // because MAX_FRAME_SIZE_IN_BYTES = MAX_INT
+ if (Q_UNLIKELY(m_payload.length() != int(m_length))) {
+ // some error occurred; refer to the Qt documentation of QIODevice::read()
+ setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
+ tr("Some serious error occurred while reading from the network."));
+ } else if (hasMask()) {
+ QWebSocketProtocol::mask(&m_payload, mask());
+ }
+ return PS_DISPATCH_RESULT;
+ }
+ return PS_WAIT_FOR_MORE_DATA;
}
/*!
diff --git a/src/websockets/qwebsocketframe_p.h b/src/websockets/qwebsocketframe_p.h
index 5c3a7df..e2b4e9f 100644
--- a/src/websockets/qwebsocketframe_p.h
+++ b/src/websockets/qwebsocketframe_p.h
@@ -101,15 +101,16 @@ public:
void clear();
bool isValid() const;
+ bool isDone() const;
- static QWebSocketFrame readFrame(QIODevice *pIoDevice);
+ void readFrame(QIODevice *pIoDevice);
private:
QWebSocketProtocol::CloseCode m_closeCode;
QString m_closeReason;
quint32 m_mask;
QWebSocketProtocol::OpCode m_opCode;
- quint8 m_length;
+ quint64 m_length;
QByteArray m_payload;
bool m_isFinalFrame;
@@ -122,12 +123,16 @@ private:
{
PS_READ_HEADER,
PS_READ_PAYLOAD_LENGTH,
- PS_READ_BIG_PAYLOAD_LENGTH,
PS_READ_MASK,
PS_READ_PAYLOAD,
PS_DISPATCH_RESULT,
PS_WAIT_FOR_MORE_DATA
- };
+ } m_processingState{PS_READ_HEADER};
+
+ ProcessingState readFrameHeader(QIODevice *pIoDevice);
+ ProcessingState readFramePayloadLength(QIODevice *pIoDevice);
+ ProcessingState readFrameMask(QIODevice *pIoDevice);
+ ProcessingState readFramePayload(QIODevice *pIoDevice);
void setError(QWebSocketProtocol::CloseCode code, const QString &closeReason);
bool checkValidity();
diff --git a/src/websockets/qwebsockethandshakeresponse.cpp b/src/websockets/qwebsockethandshakeresponse.cpp
index 383f1bf..d3ef609 100644
--- a/src/websockets/qwebsockethandshakeresponse.cpp
+++ b/src/websockets/qwebsockethandshakeresponse.cpp
@@ -162,13 +162,16 @@ QString QWebSocketHandshakeResponse::getHandshakeResponse(
if (request.isValid()) {
const QString acceptKey = calculateAcceptKey(request.key());
const QList<QString> matchingProtocols =
- listIntersection(supportedProtocols, request.protocols(), std::less<>());
+ listIntersection(supportedProtocols, request.protocols(),
+ std::less<QString>());
//TODO: extensions must be kept in the order in which they arrive
//cannot use set.intersect() to get the supported extensions
const QList<QString> matchingExtensions =
- listIntersection(supportedExtensions, request.extensions(), std::less<>());
+ listIntersection(supportedExtensions, request.extensions(),
+ std::less<QString>());
const QList<QWebSocketProtocol::Version> matchingVersions =
- listIntersection(supportedVersions, request.versions(), std::greater<>()); //sort in descending order
+ listIntersection(supportedVersions, request.versions(),
+ std::greater<QWebSocketProtocol::Version>()); //sort in descending order
if (Q_UNLIKELY(matchingVersions.isEmpty())) {
m_error = QWebSocketProtocol::CloseCodeProtocolError;
diff --git a/src/websockets/qwebsocketserver.cpp b/src/websockets/qwebsocketserver.cpp
index 9717401..eafe3fd 100644
--- a/src/websockets/qwebsocketserver.cpp
+++ b/src/websockets/qwebsocketserver.cpp
@@ -353,14 +353,26 @@ int QWebSocketServer::maxPendingConnections() const
}
/*!
+ \fn std::chrono::milliseconds QWebSocketServer::handshakeTimeout() const
Returns the handshake timeout for new connections in milliseconds.
The default is 10 seconds. If a peer uses more time to complete the
handshake their connection is closed.
- \sa setHandshakeTimeout()
+ \sa setHandshakeTimeout(), handshakeTimeoutMS()
+ \since 5.14
*/
-int QWebSocketServer::handshakeTimeout() const
+
+/*!
+ Returns the handshake timeout for new connections in milliseconds.
+
+ The default is 10 seconds. If a peer uses more time to complete the
+ handshake their connection is closed.
+
+ \sa setHandshakeTimeout(), handshakeTimeout()
+ \since 5.14
+ */
+int QWebSocketServer::handshakeTimeoutMS() const
{
Q_D(const QWebSocketServer);
return d->handshakeTimeout();
@@ -592,14 +604,20 @@ void QWebSocketServer::setMaxPendingConnections(int numConnections)
}
/*!
+ \fn void QWebSocketServer::setHandshakeTimeout(std::chrono::milliseconds msec)
Sets the handshake timeout for new connections to \a msec milliseconds.
By default this is set to 10 seconds. If a peer uses more time to
complete the handshake, their connection is closed. You can pass a
negative value (e.g. -1) to disable the timeout.
- \sa handshakeTimeout()
+ \sa handshakeTimeout(), handshakeTimeoutMS()
+ \since 5.14
*/
+
+/*!
+ \overload
+*/
void QWebSocketServer::setHandshakeTimeout(int msec)
{
Q_D(QWebSocketServer);
diff --git a/src/websockets/qwebsocketserver.h b/src/websockets/qwebsocketserver.h
index dd06448..ceb9106 100644
--- a/src/websockets/qwebsocketserver.h
+++ b/src/websockets/qwebsocketserver.h
@@ -52,6 +52,10 @@
#include <QtNetwork/QSslError>
#endif
+#if QT_HAS_INCLUDE(<chrono>)
+#include <chrono>
+#endif
+
QT_BEGIN_NAMESPACE
class QTcpSocket;
@@ -86,8 +90,18 @@ public:
void setMaxPendingConnections(int numConnections);
int maxPendingConnections() const;
+#if QT_HAS_INCLUDE(<chrono>) || defined(Q_CLANG_QDOC)
+ void setHandshakeTimeout(std::chrono::milliseconds msec)
+ {
+ setHandshakeTimeout(int(msec.count()));
+ }
+ std::chrono::milliseconds handshakeTimeout() const
+ {
+ return std::chrono::milliseconds(handshakeTimeoutMS());
+ }
+#endif
void setHandshakeTimeout(int msec);
- int handshakeTimeout() const;
+ int handshakeTimeoutMS() const;
quint16 serverPort() const;
QHostAddress serverAddress() const;