diff --git a/src/websockets/qwebsocket.cpp b/src/websockets/qwebsocket.cpp
index ade1eb4..144268f 100644
--- a/src/websockets/qwebsocket.cpp
+++ b/src/websockets/qwebsocket.cpp
@@ -788,4 +788,115 @@ qint64 QWebSocket::bytesToWrite() const
return d->m_pSocket ? d->m_pSocket->bytesToWrite() : 0;
}
+/*!
+ \since 5.15
+ Sets the maximum allowed size of an incoming websocket frame to \a maxAllowedIncomingFrameSize.
+ If an incoming frame exceeds this limit, the peer gets disconnected.
+ The accepted range is between 0 and maxIncomingFrameSize(), default is maxIncomingFrameSize().
+ The purpose of this function is to avoid exhausting virtual memory.
+
+ \sa maxAllowedIncomingFrameSize()
+ */
+void QWebSocket::setMaxAllowedIncomingFrameSize(quint64 maxAllowedIncomingFrameSize)
+{
+ Q_D(QWebSocket);
+ d->setMaxAllowedIncomingFrameSize(maxAllowedIncomingFrameSize);
+}
+
+/*!
+ \since 5.15
+ Returns the maximum allowed size of an incoming websocket frame.
+
+ \sa setMaxAllowedIncomingFrameSize()
+ */
+quint64 QWebSocket::maxAllowedIncomingFrameSize() const
+{
+ Q_D(const QWebSocket);
+ return d->maxAllowedIncomingFrameSize();
+}
+
+/*!
+ \since 5.15
+ Sets the maximum allowed size of an incoming websocket message to \a maxAllowedIncomingMessageSize.
+ If an incoming message exceeds this limit, the peer gets disconnected.
+ The accepted range is between 0 and maxIncomingMessageSize(), default is maxIncomingMessageSize().
+ The purpose of this function is to avoid exhausting virtual memory.
+
+ \sa maxAllowedIncomingMessageSize()
+ */
+void QWebSocket::setMaxAllowedIncomingMessageSize(quint64 maxAllowedIncomingMessageSize)
+{
+ Q_D(QWebSocket);
+ d->setMaxAllowedIncomingMessageSize(maxAllowedIncomingMessageSize);
+}
+
+/*!
+ \since 5.15
+ Returns the maximum allowed size of an incoming websocket message.
+
+ \sa setMaxAllowedIncomingMessageSize()
+ */
+quint64 QWebSocket::maxAllowedIncomingMessageSize() const
+{
+ Q_D(const QWebSocket);
+ return d->maxAllowedIncomingMessageSize();
+}
+
+/*!
+ \since 5.15
+ Returns the maximum supported size of an incoming websocket message for this websocket
+ implementation.
+ */
+quint64 QWebSocket::maxIncomingMessageSize()
+{
+ return QWebSocketPrivate::maxIncomingMessageSize();
+}
+
+/*!
+ \since 5.15
+ Returns the maximum supported size of an incoming websocket frame for this websocket
+ implementation.
+ */
+quint64 QWebSocket::maxIncomingFrameSize()
+{
+ return QWebSocketPrivate::maxIncomingFrameSize();
+}
+
+/*!
+ \since 5.15
+ Sets the maximum size of an outgoing websocket frame to \a outgoingFrameSize.
+ The accepted range is between 0 and maxOutgoingFrameSize(), default is 512kB.
+ The purpose of this function is to adapt to the maximum allowed frame size
+ of the receiver.
+
+ \sa outgoingFrameSize()
+ */
+void QWebSocket::setOutgoingFrameSize(quint64 outgoingFrameSize)
+{
+ Q_D(QWebSocket);
+ d->setOutgoingFrameSize(outgoingFrameSize);
+}
+
+/*!
+ \since 5.15
+ Returns the maximum size of an outgoing websocket frame.
+
+ \sa setOutgoingFrameSize()
+ */
+quint64 QWebSocket::outgoingFrameSize() const
+{
+ Q_D(const QWebSocket);
+ return d->outgoingFrameSize();
+}
+
+/*!
+ \since 5.15
+ Returns the maximum supported size of an outgoing websocket frame for this websocket
+ implementation.
+ */
+quint64 QWebSocket::maxOutgoingFrameSize()
+{
+ return QWebSocketPrivate::maxOutgoingFrameSize();
+}
+
QT_END_NAMESPACE
diff --git a/src/websockets/qwebsocket.h b/src/websockets/qwebsocket.h
index 4944689..fad565c 100644
--- a/src/websockets/qwebsocket.h
+++ b/src/websockets/qwebsocket.h
@@ -115,6 +115,17 @@ public:
qint64 bytesToWrite() const;
+ void setMaxAllowedIncomingFrameSize(quint64 maxAllowedIncomingFrameSize);
+ quint64 maxAllowedIncomingFrameSize() const;
+ void setMaxAllowedIncomingMessageSize(quint64 maxAllowedIncomingMessageSize);
+ quint64 maxAllowedIncomingMessageSize() const;
+ static quint64 maxIncomingMessageSize();
+ static quint64 maxIncomingFrameSize();
+
+ void setOutgoingFrameSize(quint64 outgoingFrameSize);
+ quint64 outgoingFrameSize() const;
+ static quint64 maxOutgoingFrameSize();
+
public Q_SLOTS:
void close(QWebSocketProtocol::CloseCode closeCode = QWebSocketProtocol::CloseCodeNormal,
const QString &reason = QString());
diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp
index 8529538..5480ecf 100644
--- a/src/websockets/qwebsocket_p.cpp
+++ b/src/websockets/qwebsocket_p.cpp
@@ -69,7 +69,8 @@
QT_BEGIN_NAMESPACE
-const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //maximum size of a frame when sending a message
+const quint64 MAX_OUTGOING_FRAME_SIZE_IN_BYTES = std::numeric_limits<int>::max() - 1;
+const quint64 DEFAULT_OUTGOING_FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //default size of a frame when sending a message
QWebSocketConfiguration::QWebSocketConfiguration() :
#ifndef QT_NO_SSL
@@ -111,7 +112,8 @@ QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::
m_configuration(),
m_pMaskGenerator(&m_defaultMaskGenerator),
m_defaultMaskGenerator(),
- m_handshakeState(NothingDoneState)
+ m_handshakeState(NothingDoneState),
+ m_outgoingFrameSize(DEFAULT_OUTGOING_FRAME_SIZE_IN_BYTES)
{
}
@@ -142,7 +144,8 @@ QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol:
m_configuration(),
m_pMaskGenerator(&m_defaultMaskGenerator),
m_defaultMaskGenerator(),
- m_handshakeState(NothingDoneState)
+ m_handshakeState(NothingDoneState),
+ m_outgoingFrameSize(DEFAULT_OUTGOING_FRAME_SIZE_IN_BYTES)
{
}
@@ -565,7 +568,7 @@ void QWebSocketPrivate::enableMasking(bool enable)
/*!
* \internal
*/
-void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
+void QWebSocketPrivate::makeConnections(QTcpSocket *pTcpSocket)
{
Q_ASSERT(pTcpSocket);
Q_Q(QWebSocket);
@@ -632,6 +635,10 @@ void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
&QWebSocketPrivate::processPong);
QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this,
&QWebSocketPrivate::processClose);
+
+ //fire readyread, in case we already have data inside the tcpSocket
+ if (pTcpSocket->bytesAvailable())
+ Q_EMIT pTcpSocket->readyRead();
}
/*!
@@ -768,11 +775,11 @@ qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
QWebSocketProtocol::OpCodeBinary : QWebSocketProtocol::OpCodeText;
- int numFrames = data.size() / int(FRAME_SIZE_IN_BYTES);
+ int numFrames = data.size() / int(outgoingFrameSize());
QByteArray tmpData(data);
tmpData.detach();
char *payload = tmpData.data();
- quint64 sizeLeft = quint64(data.size()) % FRAME_SIZE_IN_BYTES;
+ quint64 sizeLeft = quint64(data.size()) % outgoingFrameSize();
if (Q_LIKELY(sizeLeft))
++numFrames;
@@ -791,7 +798,7 @@ qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
const bool isLastFrame = (i == (numFrames - 1));
const bool isFirstFrame = (i == 0);
- const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
+ const quint64 size = qMin(bytesLeft, outgoingFrameSize());
const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode
: QWebSocketProtocol::OpCodeContinue;
@@ -1174,10 +1181,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;
}
}
}
@@ -1301,6 +1308,80 @@ void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
}
}
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setMaxAllowedIncomingFrameSize(quint64 maxAllowedIncomingFrameSize)
+{
+ m_dataProcessor.setMaxAllowedFrameSize(maxAllowedIncomingFrameSize);
+}
+
+/*!
+ \internal
+ */
+quint64 QWebSocketPrivate::maxAllowedIncomingFrameSize() const
+{
+ return m_dataProcessor.maxAllowedFrameSize();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setMaxAllowedIncomingMessageSize(quint64 maxAllowedIncomingMessageSize)
+{
+ m_dataProcessor.setMaxAllowedMessageSize(maxAllowedIncomingMessageSize);
+}
+
+/*!
+ \internal
+ */
+quint64 QWebSocketPrivate::maxAllowedIncomingMessageSize() const
+{
+ return m_dataProcessor.maxAllowedMessageSize();
+}
+
+/*!
+ \internal
+ */
+quint64 QWebSocketPrivate::maxIncomingMessageSize()
+{
+ return QWebSocketDataProcessor::maxMessageSize();
+}
+
+/*!
+ \internal
+ */
+quint64 QWebSocketPrivate::maxIncomingFrameSize()
+{
+ return QWebSocketDataProcessor::maxFrameSize();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setOutgoingFrameSize(quint64 outgoingFrameSize)
+{
+ if (outgoingFrameSize <= maxOutgoingFrameSize())
+ m_outgoingFrameSize = outgoingFrameSize;
+}
+
+/*!
+ \internal
+ */
+quint64 QWebSocketPrivate::outgoingFrameSize() const
+{
+ return m_outgoingFrameSize;
+}
+
+/*!
+ \internal
+ */
+quint64 QWebSocketPrivate::maxOutgoingFrameSize()
+{
+ return MAX_OUTGOING_FRAME_SIZE_IN_BYTES;
+}
+
+
/*!
\internal
*/
diff --git a/src/websockets/qwebsocket_p.h b/src/websockets/qwebsocket_p.h
index 4b39dfc..c3f170d 100644
--- a/src/websockets/qwebsocket_p.h
+++ b/src/websockets/qwebsocket_p.h
@@ -160,6 +160,17 @@ public:
void ping(const QByteArray &payload);
void setSocketState(QAbstractSocket::SocketState state);
+ void setMaxAllowedIncomingFrameSize(quint64 maxAllowedIncomingFrameSize);
+ quint64 maxAllowedIncomingFrameSize() const;
+ void setMaxAllowedIncomingMessageSize(quint64 maxAllowedIncomingMessageSize);
+ quint64 maxAllowedIncomingMessageSize() const;
+ static quint64 maxIncomingMessageSize();
+ static quint64 maxIncomingFrameSize();
+
+ void setOutgoingFrameSize(quint64 outgoingFrameSize);
+ quint64 outgoingFrameSize() const;
+ static quint64 maxOutgoingFrameSize();
+
private:
QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version);
void setVersion(QWebSocketProtocol::Version version);
@@ -182,7 +193,7 @@ private:
Q_REQUIRED_RESULT qint64 doWriteFrames(const QByteArray &data, bool isBinary);
- void makeConnections(const QTcpSocket *pTcpSocket);
+ void makeConnections(QTcpSocket *pTcpSocket);
void releaseConnections(const QTcpSocket *pTcpSocket);
QByteArray getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength,
@@ -250,6 +261,8 @@ private:
QString m_httpStatusMessage;
QMap<QString, QString> m_headers;
+ quint64 m_outgoingFrameSize;
+
friend class QWebSocketServerPrivate;
#ifdef Q_OS_WASM
emscripten::val socketContext = emscripten::val::null();
diff --git a/src/websockets/qwebsocketdataprocessor.cpp b/src/websockets/qwebsocketdataprocessor.cpp
index 4f81222..4110f2a 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);
}
/*!
@@ -101,6 +105,33 @@ QWebSocketDataProcessor::~QWebSocketDataProcessor()
}
}
+void QWebSocketDataProcessor::setMaxAllowedFrameSize(quint64 maxAllowedFrameSize)
+{
+ frame.setMaxAllowedFrameSize(maxAllowedFrameSize);
+}
+
+quint64 QWebSocketDataProcessor::maxAllowedFrameSize() const
+{
+ return frame.maxAllowedFrameSize();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketDataProcessor::setMaxAllowedMessageSize(quint64 maxAllowedMessageSize)
+{
+ if (maxAllowedMessageSize <= maxMessageSize())
+ m_maxAllowedMessageSize = maxAllowedMessageSize;
+}
+
+/*!
+ \internal
+ */
+quint64 QWebSocketDataProcessor::maxAllowedMessageSize() const
+{
+ return m_maxAllowedMessageSize;
+}
+
/*!
\internal
*/
@@ -114,19 +145,28 @@ quint64 QWebSocketDataProcessor::maxMessageSize()
*/
quint64 QWebSocketDataProcessor::maxFrameSize()
{
- return MAX_FRAME_SIZE_IN_BYTES;
+ return QWebSocketFrame::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 +176,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 +184,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();
@@ -154,11 +194,11 @@ void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
? quint64(m_textMessage.length())
: quint64(m_binaryMessage.length());
if (Q_UNLIKELY((messageLength + quint64(frame.payload().length())) >
- MAX_MESSAGE_SIZE_IN_BYTES)) {
+ maxAllowedMessageSize())) {
clear();
Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeTooMuchData,
tr("Received message is too big."));
- return;
+ return true;
}
if (m_opCode == QWebSocketProtocol::OpCodeText) {
@@ -171,7 +211,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 +239,9 @@ void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
clear();
isDone = true;
}
+ frame.clear();
}
+ return true;
}
/*!
@@ -301,4 +343,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..62a2dc0 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"
@@ -63,6 +65,8 @@ QT_BEGIN_NAMESPACE
class QIODevice;
class QWebSocketFrame;
+const quint64 MAX_MESSAGE_SIZE_IN_BYTES = std::numeric_limits<int>::max() - 1;
+
class Q_AUTOTEST_EXPORT QWebSocketDataProcessor : public QObject
{
Q_OBJECT
@@ -72,6 +76,10 @@ public:
explicit QWebSocketDataProcessor(QObject *parent = nullptr);
~QWebSocketDataProcessor() override;
+ void setMaxAllowedFrameSize(quint64 maxAllowedFrameSize);
+ quint64 maxAllowedFrameSize() const;
+ void setMaxAllowedMessageSize(quint64 maxAllowedMessageSize);
+ quint64 maxAllowedMessageSize() const;
static quint64 maxMessageSize();
static quint64 maxFrameSize();
@@ -86,7 +94,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 +119,12 @@ private:
quint64 m_payloadLength;
QTextCodec::ConverterState *m_pConverterState;
QTextCodec *m_pTextCodec;
+ QWebSocketFrame frame;
+ QTimer waitTimer;
+ quint64 m_maxAllowedMessageSize = MAX_MESSAGE_SIZE_IN_BYTES;
bool processControlFrame(const QWebSocketFrame &frame);
+ void timeout();
};
QT_END_NAMESPACE
diff --git a/src/websockets/qwebsocketframe.cpp b/src/websockets/qwebsocketframe.cpp
index 041302e..716aebd 100644
--- a/src/websockets/qwebsocketframe.cpp
+++ b/src/websockets/qwebsocketframe.cpp
@@ -64,118 +64,26 @@ QT_BEGIN_NAMESPACE
/*!
\internal
*/
-QWebSocketFrame::QWebSocketFrame() :
- m_closeCode(QWebSocketProtocol::CloseCodeNormal),
- m_closeReason(),
- m_mask(0),
- m_opCode(QWebSocketProtocol::OpCodeReservedC),
- m_length(0),
- m_payload(),
- m_isFinalFrame(true),
- m_rsv1(false),
- m_rsv2(false),
- m_rsv3(false),
- m_isValid(false)
+void QWebSocketFrame::setMaxAllowedFrameSize(quint64 maxAllowedFrameSize)
{
+ if (maxAllowedFrameSize <= maxFrameSize())
+ m_maxAllowedFrameSize = maxAllowedFrameSize;
}
/*!
\internal
*/
-QWebSocketFrame::QWebSocketFrame(const QWebSocketFrame &other) :
- m_closeCode(other.m_closeCode),
- m_closeReason(other.m_closeReason),
- m_mask(other.m_mask),
- m_opCode(other.m_opCode),
- m_length(other.m_length),
- m_payload(other.m_payload),
- m_isFinalFrame(other.m_isFinalFrame),
- m_rsv1(other.m_rsv1),
- m_rsv2(other.m_rsv2),
- m_rsv3(other.m_rsv3),
- m_isValid(other.m_isValid)
+quint64 QWebSocketFrame::maxAllowedFrameSize() const
{
+ return m_maxAllowedFrameSize;
}
/*!
\internal
*/
-QWebSocketFrame &QWebSocketFrame::operator =(const QWebSocketFrame &other)
+quint64 QWebSocketFrame::maxFrameSize()
{
- m_closeCode = other.m_closeCode;
- m_closeReason = other.m_closeReason;
- m_isFinalFrame = other.m_isFinalFrame;
- m_mask = other.m_mask;
- m_rsv1 = other.m_rsv1;
- m_rsv2 = other.m_rsv2;
- m_rsv3 = other.m_rsv3;
- m_opCode = other.m_opCode;
- m_length = other.m_length;
- m_payload = other.m_payload;
- m_isValid = other.m_isValid;
-
- return *this;
-}
-
-#ifdef Q_COMPILER_RVALUE_REFS
-/*!
- \internal
- */
-QWebSocketFrame::QWebSocketFrame(QWebSocketFrame &&other) :
- m_closeCode(qMove(other.m_closeCode)),
- m_closeReason(qMove(other.m_closeReason)),
- m_mask(qMove(other.m_mask)),
- m_opCode(qMove(other.m_opCode)),
- m_length(qMove(other.m_length)),
- m_payload(qMove(other.m_payload)),
- m_isFinalFrame(qMove(other.m_isFinalFrame)),
- m_rsv1(qMove(other.m_rsv1)),
- m_rsv2(qMove(other.m_rsv2)),
- m_rsv3(qMove(other.m_rsv3)),
- m_isValid(qMove(other.m_isValid))
-{}
-
-
-/*!
- \internal
- */
-QWebSocketFrame &QWebSocketFrame::operator =(QWebSocketFrame &&other)
-{
- qSwap(m_closeCode, other.m_closeCode);
- qSwap(m_closeReason, other.m_closeReason);
- qSwap(m_isFinalFrame, other.m_isFinalFrame);
- qSwap(m_mask, other.m_mask);
- qSwap(m_rsv1, other.m_rsv1);
- qSwap(m_rsv2, other.m_rsv2);
- qSwap(m_rsv3, other.m_rsv3);
- qSwap(m_opCode, other.m_opCode);
- qSwap(m_length, other.m_length);
- qSwap(m_payload, other.m_payload);
- qSwap(m_isValid, other.m_isValid);
-
- return *this;
-}
-
-#endif
-
-/*!
- \internal
- */
-void QWebSocketFrame::swap(QWebSocketFrame &other)
-{
- if (&other != this) {
- qSwap(m_closeCode, other.m_closeCode);
- qSwap(m_closeReason, other.m_closeReason);
- qSwap(m_isFinalFrame, other.m_isFinalFrame);
- qSwap(m_mask, other.m_mask);
- qSwap(m_rsv1, other.m_rsv1);
- qSwap(m_rsv2, other.m_rsv2);
- qSwap(m_rsv3, other.m_rsv3);
- qSwap(m_opCode, other.m_opCode);
- qSwap(m_length, other.m_length);
- qSwap(m_payload, other.m_payload);
- qSwap(m_isValid, other.m_isValid);
- }
+ return MAX_FRAME_SIZE_IN_BYTES;
}
/*!
@@ -183,7 +91,7 @@ void QWebSocketFrame::swap(QWebSocketFrame &other)
*/
QWebSocketProtocol::CloseCode QWebSocketFrame::closeCode() const
{
- return m_closeCode;
+ return isDone() ? m_closeCode : QWebSocketProtocol::CloseCodeGoingAway;
}
/*!
@@ -191,7 +99,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 +184,7 @@ void QWebSocketFrame::clear()
m_length = 0;
m_payload.clear();
m_isValid = false;
+ m_processingState = PS_READ_HEADER;
}
/*!
@@ -283,215 +192,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;
+ }
}
+}
- return frame;
+/*!
+ \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;
+}
+
+/*!
+ \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 > maxAllowedFrameSize())) {
+ 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..992379a 100644
--- a/src/websockets/qwebsocketframe_p.h
+++ b/src/websockets/qwebsocketframe_p.h
@@ -54,7 +54,7 @@
#include <QtCore/QString>
#include <QtCore/QByteArray>
#include <QtCore/QCoreApplication>
-#include <limits.h>
+#include <limits>
#include "qwebsockets_global.h"
#include "qwebsocketprotocol.h"
@@ -64,25 +64,18 @@ QT_BEGIN_NAMESPACE
class QIODevice;
-const quint64 MAX_FRAME_SIZE_IN_BYTES = INT_MAX - 1;
-const quint64 MAX_MESSAGE_SIZE_IN_BYTES = INT_MAX - 1;
+const quint64 MAX_FRAME_SIZE_IN_BYTES = std::numeric_limits<int>::max() - 1;
class Q_AUTOTEST_EXPORT QWebSocketFrame
{
Q_DECLARE_TR_FUNCTIONS(QWebSocketFrame)
public:
- QWebSocketFrame();
- QWebSocketFrame(const QWebSocketFrame &other);
+ QWebSocketFrame() = default;
- QWebSocketFrame &operator =(const QWebSocketFrame &other);
-
-#ifdef Q_COMPILER_RVALUE_REFS
- QWebSocketFrame(QWebSocketFrame &&other);
- QWebSocketFrame &operator =(QWebSocketFrame &&other);
-#endif
-
- void swap(QWebSocketFrame &other);
+ void setMaxAllowedFrameSize(quint64 maxAllowedFrameSize);
+ quint64 maxAllowedFrameSize() const;
+ static quint64 maxFrameSize();
QWebSocketProtocol::CloseCode closeCode() const;
QString closeReason() const;
@@ -101,33 +94,39 @@ 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;
QByteArray m_payload;
-
- bool m_isFinalFrame;
- bool m_rsv1;
- bool m_rsv2;
- bool m_rsv3;
- bool m_isValid;
+ quint64 m_length = 0;
+ quint32 m_mask = 0;
+ QWebSocketProtocol::CloseCode m_closeCode = QWebSocketProtocol::CloseCodeNormal;
+ QWebSocketProtocol::OpCode m_opCode = QWebSocketProtocol::OpCodeReservedC;
enum ProcessingState
{
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;
+
+ bool m_isFinalFrame = true;
+ bool m_rsv1 = false;
+ bool m_rsv2 = false;
+ bool m_rsv3 = false;
+ bool m_isValid = false;
+ quint64 m_maxAllowedFrameSize = MAX_FRAME_SIZE_IN_BYTES;
+
+ 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/qwebsocketserver_p.cpp b/src/websockets/qwebsocketserver_p.cpp
index f3e7eac..d9d79d9 100644
--- a/src/websockets/qwebsocketserver_p.cpp
+++ b/src/websockets/qwebsocketserver_p.cpp
@@ -416,9 +416,25 @@ void QWebSocketServerPrivate::handshakeReceived()
//For Safari, the handshake is delivered at once
//FIXME: For FireFox, the readyRead signal is never emitted
//This is a bug in FireFox (see https://bugzilla.mozilla.org/show_bug.cgi?id=594502)
- if (!pTcpSocket->canReadLine()) {
+ // According to RFC822 the body is separated from the headers by a null line (CRLF)
+ const QByteArray& endOfHeaderMarker = QByteArrayLiteral("\r\n\r\n");
+
+ const qint64 byteAvailable = pTcpSocket->bytesAvailable();
+ QByteArray header = pTcpSocket->peek(byteAvailable);
+ const int endOfHeaderIndex = header.indexOf(endOfHeaderMarker);
+ if (endOfHeaderIndex < 0) {
+ //then we don't have our header complete yet
+ //check that no one is trying to exhaust our virtual memory
+ const qint64 maxHeaderLength = MAX_HEADERLINE_LENGTH * MAX_HEADERLINES + endOfHeaderMarker.size();
+ if (byteAvailable > maxHeaderLength) {
+ pTcpSocket->close();
+ setError(QWebSocketProtocol::CloseCodeTooMuchData,
+ QWebSocketServer::tr("Header is too large."));
+ }
return;
}
+ const int headerSize = endOfHeaderIndex + endOfHeaderMarker.size();
+
disconnect(pTcpSocket, &QTcpSocket::readyRead,
this, &QWebSocketServerPrivate::handshakeReceived);
Q_Q(QWebSocketServer);
@@ -433,8 +449,20 @@ void QWebSocketServerPrivate::handshakeReceived()
return;
}
+ //don't read past the header
+ header.resize(headerSize);
+ //remove our header from the tcpSocket
+ qint64 skippedSize = pTcpSocket->skip(headerSize);
+
+ if (skippedSize != headerSize) {
+ pTcpSocket->close();
+ setError(QWebSocketProtocol::CloseCodeProtocolError,
+ QWebSocketServer::tr("Read handshake request header failed."));
+ return;
+ }
+
QWebSocketHandshakeRequest request(pTcpSocket->peerPort(), isSecure);
- QTextStream textStream(pTcpSocket);
+ QTextStream textStream(header, QIODevice::ReadOnly);
request.readHandshake(textStream, MAX_HEADERLINE_LENGTH, MAX_HEADERLINES);
if (request.isValid()) {
@@ -487,11 +515,13 @@ void QWebSocketServerPrivate::handleConnection(QTcpSocket *pTcpSocket) const
QObjectPrivate::connect(pTcpSocket, &QTcpSocket::readyRead,
this, &QWebSocketServerPrivate::handshakeReceived,
Qt::QueuedConnection);
- if (pTcpSocket->canReadLine()) {
- // We received some data! We must emit now to be sure that handshakeReceived is called
- // since the data could have been received before the signal and slot was connected.
- emit pTcpSocket->readyRead();
+
+ // We received some data! We must emit now to be sure that handshakeReceived is called
+ // since the data could have been received before the signal and slot was connected.
+ if (pTcpSocket->bytesAvailable()) {
+ Q_EMIT pTcpSocket->readyRead();
}
+
QObjectPrivate::connect(pTcpSocket, &QTcpSocket::disconnected,
this, &QWebSocketServerPrivate::onSocketDisconnected);
}
diff --git a/tests/auto/websockets/dataprocessor/tst_dataprocessor.cpp b/tests/auto/websockets/dataprocessor/tst_dataprocessor.cpp
index f9a91d5..5390ff0 100644
--- a/tests/auto/websockets/dataprocessor/tst_dataprocessor.cpp
+++ b/tests/auto/websockets/dataprocessor/tst_dataprocessor.cpp
@@ -193,7 +193,7 @@ private:
//sequences
void nonCharacterSequence(const char *sequence);
- void doTest();
+ void doTest(int timeout = 0);
void doCloseFrameTest();
QString opCodeToString(quint8 opCode);
@@ -744,6 +744,7 @@ void tst_DataProcessor::frameTooSmall()
dataProcessor.process(&buffer);
+ QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000);
QCOMPARE(errorSpy.count(), 1);
QCOMPARE(closeSpy.count(), 0);
QCOMPARE(pingMessageSpy.count(), 0);
@@ -776,6 +777,7 @@ void tst_DataProcessor::frameTooSmall()
dataProcessor.process(&buffer);
+ QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000);
QCOMPARE(errorSpy.count(), 1);
QCOMPARE(closeSpy.count(), 0);
QCOMPARE(pingMessageSpy.count(), 0);
@@ -808,6 +810,24 @@ void tst_DataProcessor::frameTooSmall()
dataProcessor.process(&buffer);
+ QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000);
+ QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(closeSpy.count(), 0);
+ QCOMPARE(pingMessageSpy.count(), 0);
+ QCOMPARE(pongMessageSpy.count(), 0);
+ QCOMPARE(textMessageSpy.count(), 0);
+ QCOMPARE(binaryMessageSpy.count(), 0);
+ QCOMPARE(textFrameSpy.count(), 1);
+ QCOMPARE(binaryFrameSpy.count(), 0);
+
+ errorSpy.clear();
+ closeSpy.clear();
+ pingMessageSpy.clear();
+ pongMessageSpy.clear();
+ textMessageSpy.clear();
+ binaryMessageSpy.clear();
+ textFrameSpy.clear();
+ binaryFrameSpy.clear();
buffer.close();
data.clear();
@@ -816,17 +836,16 @@ void tst_DataProcessor::frameTooSmall()
//meaning the socket will be closed
buffer.setData(data);
buffer.open(QIODevice::ReadOnly);
- QSignalSpy errorSpy(&dataProcessor,
- SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
dataProcessor.process(&buffer);
+ QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000);
QCOMPARE(errorSpy.count(), 1);
QCOMPARE(closeSpy.count(), 0);
QCOMPARE(pingMessageSpy.count(), 0);
QCOMPARE(pongMessageSpy.count(), 0);
QCOMPARE(textMessageSpy.count(), 0);
QCOMPARE(binaryMessageSpy.count(), 0);
- QCOMPARE(textFrameSpy.count(), 1);
+ QCOMPARE(textFrameSpy.count(), 0);
QCOMPARE(binaryFrameSpy.count(), 0);
QList<QVariant> arguments = errorSpy.takeFirst();
@@ -849,6 +868,7 @@ void tst_DataProcessor::frameTooSmall()
buffer.open(QIODevice::ReadOnly);
dataProcessor.process(&buffer);
+ QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000);
QCOMPARE(errorSpy.count(), 1);
QCOMPARE(closeSpy.count(), 0);
QCOMPARE(pingMessageSpy.count(), 0);
@@ -877,6 +897,7 @@ void tst_DataProcessor::frameTooSmall()
buffer.open(QIODevice::ReadOnly);
dataProcessor.process(&buffer);
+ QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000);
QCOMPARE(errorSpy.count(), 1);
QCOMPARE(closeSpy.count(), 0);
QCOMPARE(pingMessageSpy.count(), 0);
@@ -1400,7 +1421,7 @@ void tst_DataProcessor::incompletePayload_data()
void tst_DataProcessor::incompletePayload()
{
- doTest();
+ doTest(7000);
}
void tst_DataProcessor::incompleteSizeField_data()
@@ -1430,13 +1451,13 @@ void tst_DataProcessor::incompleteSizeField_data()
void tst_DataProcessor::incompleteSizeField()
{
- doTest();
+ doTest(7000);
}
//////////////////////////////////////////////////////////////////////////////////////////
/// HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////
-void tst_DataProcessor::doTest()
+void tst_DataProcessor::doTest(int timeout)
{
QFETCH(quint8, firstByte);
QFETCH(quint8, secondByte);
@@ -1465,6 +1486,7 @@ void tst_DataProcessor::doTest()
buffer.setData(data);
buffer.open(QIODevice::ReadOnly);
dataProcessor.process(&buffer);
+ QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), timeout);
QCOMPARE(errorSpy.count(), 1);
QCOMPARE(textMessageSpy.count(), 0);
QCOMPARE(binaryMessageSpy.count(), 0);
diff --git a/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp b/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp
index 2bb5d16..aca3f40 100644
--- a/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp
+++ b/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp
@@ -39,7 +39,9 @@ class EchoServer : public QObject
{
Q_OBJECT
public:
- explicit EchoServer(QObject *parent = nullptr);
+ explicit EchoServer(QObject *parent = nullptr,
+ quint64 maxAllowedIncomingMessageSize = QWebSocket::maxIncomingMessageSize(),
+ quint64 maxAllowedIncomingFrameSize = QWebSocket::maxIncomingFrameSize());
~EchoServer();
QHostAddress hostAddress() const { return m_pWebSocketServer->serverAddress(); }
@@ -57,13 +59,17 @@ private Q_SLOTS:
private:
QWebSocketServer *m_pWebSocketServer;
+ quint64 m_maxAllowedIncomingMessageSize;
+ quint64 m_maxAllowedIncomingFrameSize;
QList<QWebSocket *> m_clients;
};
-EchoServer::EchoServer(QObject *parent) :
+EchoServer::EchoServer(QObject *parent, quint64 maxAllowedIncomingMessageSize, quint64 maxAllowedIncomingFrameSize) :
QObject(parent),
m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Echo Server"),
QWebSocketServer::NonSecureMode, this)),
+ m_maxAllowedIncomingMessageSize(maxAllowedIncomingMessageSize),
+ m_maxAllowedIncomingFrameSize(maxAllowedIncomingFrameSize),
m_clients()
{
if (m_pWebSocketServer->listen(QHostAddress(QStringLiteral("127.0.0.1")))) {
@@ -82,6 +88,9 @@ void EchoServer::onNewConnection()
{
QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
+ pSocket->setMaxAllowedIncomingFrameSize(m_maxAllowedIncomingFrameSize);
+ pSocket->setMaxAllowedIncomingMessageSize(m_maxAllowedIncomingMessageSize);
+
Q_EMIT newConnection(pSocket->requestUrl());
Q_EMIT newConnection(pSocket->request());
@@ -144,6 +153,9 @@ private Q_SLOTS:
void tst_setProxy();
#endif
void overlongCloseReason();
+ void incomingMessageTooLong();
+ void incomingFrameTooLong();
+ void testingFrameAndMessageSizeApi();
};
tst_QWebSocket::tst_QWebSocket()
@@ -457,12 +469,46 @@ void tst_QWebSocket::tst_sendTextMessage()
QVERIFY(isLastFrame);
socket.close();
+ socketConnectedSpy.clear();
+ textMessageReceived.clear();
+ textFrameReceived.clear();
- //QTBUG-36762: QWebSocket emits multiplied signals when socket was reopened
+ // QTBUG-74464 QWebsocket doesn't receive text (binary) message with size > 32 kb
+ socket.open(url);
+
+ QTRY_COMPARE(socketConnectedSpy.count(), 1);
+ QCOMPARE(socketError.count(), 0);
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ arguments = serverConnectedSpy.takeFirst();
+ urlConnected = arguments.at(0).toUrl();
+ QCOMPARE(urlConnected, url);
+ QCOMPARE(socket.bytesToWrite(), 0);
+
+ // transmit a long text message with 1 MB
+ QString longString(0x100000, 'a');
+ socket.sendTextMessage(longString);
+ QVERIFY(socket.bytesToWrite() > longString.length());
+ QVERIFY(textMessageReceived.wait());
+ QCOMPARE(socket.bytesToWrite(), 0);
+
+ QCOMPARE(textMessageReceived.count(), 1);
+ QCOMPARE(binaryMessageReceived.count(), 0);
+ QCOMPARE(binaryFrameReceived.count(), 0);
+ arguments = textMessageReceived.takeFirst();
+ messageReceived = arguments.at(0).toString();
+ QCOMPARE(messageReceived.length(), longString.length());
+ QCOMPARE(messageReceived, longString);
+
+ arguments = textFrameReceived.takeLast();
+ isLastFrame = arguments.at(1).toBool();
+ QVERIFY(isLastFrame);
+
+ socket.close();
socketConnectedSpy.clear();
textMessageReceived.clear();
textFrameReceived.clear();
+ //QTBUG-36762: QWebSocket emits multiplied signals when socket was reopened
socket.open(QUrl(QStringLiteral("ws://") + echoServer.hostAddress().toString() +
QStringLiteral(":") + QString::number(echoServer.port())));
@@ -761,6 +807,80 @@ void tst_QWebSocket::overlongCloseReason()
QCOMPARE(socket.closeReason(), reason.leftRef(123));
QTRY_COMPARE(socketDisconnectedSpy.count(), 1);
}
+
+void tst_QWebSocket::incomingMessageTooLong()
+{
+//QTBUG-70693
+ quint64 maxAllowedIncomingMessageSize = 1024;
+ quint64 maxAllowedIncomingFrameSize = QWebSocket::maxIncomingFrameSize();
+
+ EchoServer echoServer(nullptr, maxAllowedIncomingMessageSize, maxAllowedIncomingFrameSize);
+
+ QWebSocket socket;
+
+ QSignalSpy socketConnectedSpy(&socket, &QWebSocket::connected);
+ QSignalSpy serverConnectedSpy(&echoServer, QOverload<QUrl>::of(&EchoServer::newConnection));
+ QSignalSpy socketDisconnectedSpy(&socket, &QWebSocket::disconnected);
+
+ QUrl url = QUrl(QStringLiteral("ws://") + echoServer.hostAddress().toString() +
+ QStringLiteral(":") + QString::number(echoServer.port()));
+ socket.open(url);
+ QTRY_COMPARE(socketConnectedSpy.count(), 1);
+ QTRY_COMPARE(serverConnectedSpy.count(), 1);
+
+ QString payload(maxAllowedIncomingMessageSize+1, 'a');
+ QCOMPARE(socket.sendTextMessage(payload), payload.size());
+
+ QTRY_COMPARE(socketDisconnectedSpy.count(), 1);
+ QCOMPARE(socket.closeCode(), QWebSocketProtocol::CloseCodeTooMuchData);
+}
+
+void tst_QWebSocket::incomingFrameTooLong()
+{
+//QTBUG-70693
+ quint64 maxAllowedIncomingMessageSize = QWebSocket::maxIncomingMessageSize();
+ quint64 maxAllowedIncomingFrameSize = 1024;
+
+ EchoServer echoServer(nullptr, maxAllowedIncomingMessageSize, maxAllowedIncomingFrameSize);
+
+ QWebSocket socket;
+ socket.setOutgoingFrameSize(maxAllowedIncomingFrameSize+1);
+
+ QSignalSpy socketConnectedSpy(&socket, &QWebSocket::connected);
+ QSignalSpy serverConnectedSpy(&echoServer, QOverload<QUrl>::of(&EchoServer::newConnection));
+ QSignalSpy socketDisconnectedSpy(&socket, &QWebSocket::disconnected);
+
+ QUrl url = QUrl(QStringLiteral("ws://") + echoServer.hostAddress().toString() +
+ QStringLiteral(":") + QString::number(echoServer.port()));
+ socket.open(url);
+ QTRY_COMPARE(socketConnectedSpy.count(), 1);
+ QTRY_COMPARE(serverConnectedSpy.count(), 1);
+
+ QString payload(maxAllowedIncomingFrameSize+1, 'a');
+ QCOMPARE(socket.sendTextMessage(payload), payload.size());
+
+ QTRY_COMPARE(socketDisconnectedSpy.count(), 1);
+ QCOMPARE(socket.closeCode(), QWebSocketProtocol::CloseCodeTooMuchData);
+}
+
+void tst_QWebSocket::testingFrameAndMessageSizeApi()
+{
+//requested by André Hartmann, QTBUG-70693
+ QWebSocket socket;
+
+ const quint64 outgoingFrameSize = 5;
+ socket.setOutgoingFrameSize(outgoingFrameSize);
+ QTRY_COMPARE(outgoingFrameSize, socket.outgoingFrameSize());
+
+ const quint64 maxAllowedIncomingFrameSize = 9;
+ socket.setMaxAllowedIncomingFrameSize(maxAllowedIncomingFrameSize);
+ QTRY_COMPARE(maxAllowedIncomingFrameSize, socket.maxAllowedIncomingFrameSize());
+
+ const quint64 maxAllowedIncomingMessageSize = 889;
+ socket.setMaxAllowedIncomingMessageSize(maxAllowedIncomingMessageSize);
+ QTRY_COMPARE(maxAllowedIncomingMessageSize, socket.maxAllowedIncomingMessageSize());
+}
+
#endif // QT_NO_NETWORKPROXY
QTEST_MAIN(tst_QWebSocket)
diff --git a/tests/auto/websockets/websocketframe/tst_websocketframe.cpp b/tests/auto/websockets/websocketframe/tst_websocketframe.cpp
index 5614df8..7046657 100644
--- a/tests/auto/websockets/websocketframe/tst_websocketframe.cpp
+++ b/tests/auto/websockets/websocketframe/tst_websocketframe.cpp
@@ -197,11 +197,12 @@ void tst_WebSocketFrame::tst_copyConstructorAndAssignment()
QBuffer buffer(&payload);
buffer.open(QIODevice::ReadOnly);
- QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
+ QWebSocketFrame frame;
+ frame.readFrame(&buffer);
buffer.close();
+ auto compareFrames = [](const QWebSocketFrame &other, const QWebSocketFrame &frame)
{
- QWebSocketFrame other(frame);
QCOMPARE(other.closeCode(), frame.closeCode());
QCOMPARE(other.closeReason(), frame.closeReason());
QCOMPARE(other.hasMask(), frame.hasMask());
@@ -216,24 +217,20 @@ void tst_WebSocketFrame::tst_copyConstructorAndAssignment()
QCOMPARE(other.rsv1(), frame.rsv1());
QCOMPARE(other.rsv2(), frame.rsv2());
QCOMPARE(other.rsv3(), frame.rsv3());
+ };
+
+ {
+ QWebSocketFrame other(frame);
+ compareFrames(other, frame);
}
{
QWebSocketFrame other;
other = frame;
- QCOMPARE(other.closeCode(), frame.closeCode());
- QCOMPARE(other.closeReason(), frame.closeReason());
- QCOMPARE(other.hasMask(), frame.hasMask());
- QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
- QCOMPARE(other.isControlFrame(), frame.isControlFrame());
- QCOMPARE(other.isDataFrame(), frame.isDataFrame());
- QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
- QCOMPARE(other.isValid(), frame.isValid());
- QCOMPARE(other.mask(), frame.mask());
- QCOMPARE(other.opCode(), frame.opCode());
- QCOMPARE(other.payload(), frame.payload());
- QCOMPARE(other.rsv1(), frame.rsv1());
- QCOMPARE(other.rsv2(), frame.rsv2());
- QCOMPARE(other.rsv3(), frame.rsv3());
+ compareFrames(other, frame);
+ QWebSocketFrame other2 = std::move(other);
+ compareFrames(other2, frame);
+ QWebSocketFrame other3(std::move(other2));
+ compareFrames(other3, frame);
}
}
@@ -330,7 +327,8 @@ void tst_WebSocketFrame::tst_goodFrames()
QBuffer buffer;
buffer.setData(wireRepresentation);
buffer.open(QIODevice::ReadOnly);
- QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
+ QWebSocketFrame frame;
+ frame.readFrame(&buffer);
buffer.close();
QVERIFY(frame.isValid());
QCOMPARE(frame.rsv1(), rsv1);
@@ -495,7 +493,8 @@ void tst_WebSocketFrame::tst_invalidFrames()
QBuffer buffer;
buffer.setData(wireRepresentation);
buffer.open(QIODevice::ReadOnly);
- QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
+ QWebSocketFrame frame;
+ frame.readFrame(&buffer);
buffer.close();
QVERIFY(!frame.isValid());
@@ -542,7 +541,7 @@ void tst_WebSocketFrame::tst_malformedFrames_data()
//too much data
{
const char bigpayloadIndicator = char(127);
- const quint64 payloadSize = MAX_FRAME_SIZE_IN_BYTES + 1;
+ const quint64 payloadSize = QWebSocketFrame::maxFrameSize() + 1;
uchar swapped[8] = {0};
qToBigEndian<quint64>(payloadSize, swapped);
QTest::newRow("Frame too big")
@@ -606,7 +605,8 @@ void tst_WebSocketFrame::tst_malformedFrames()
QBuffer buffer;
buffer.setData(payload);
buffer.open(QIODevice::ReadOnly);
- QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
+ QWebSocketFrame frame;
+ frame.readFrame(&buffer);
buffer.close();
QVERIFY(!frame.isValid());