diff --git a/.gitignore b/.gitignore index d85dfe4..326e1ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/qtwebsockets-everywhere-src-5.12.5.tar.xz +SOURCES/qtwebsockets-everywhere-src-5.15.2.tar.xz diff --git a/.qt5-qtwebsockets.metadata b/.qt5-qtwebsockets.metadata index be7e9c8..ddaa7fc 100644 --- a/.qt5-qtwebsockets.metadata +++ b/.qt5-qtwebsockets.metadata @@ -1 +1 @@ -7d38f6bd74ec5f0498cd567925317a3e94c6d779 SOURCES/qtwebsockets-everywhere-src-5.12.5.tar.xz +cf1a77a16ba0babad9a48a72f78f6f019a4767c8 SOURCES/qtwebsockets-everywhere-src-5.15.2.tar.xz diff --git a/SOURCES/qtwebsockets-add-public-api-to-set-max-frame-and-message-size.patch b/SOURCES/qtwebsockets-add-public-api-to-set-max-frame-and-message-size.patch deleted file mode 100644 index 5056c5a..0000000 --- a/SOURCES/qtwebsockets-add-public-api-to-set-max-frame-and-message-size.patch +++ /dev/null @@ -1,1672 +0,0 @@ -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::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 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 - #include - #include -+#include -+#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::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(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(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(reinterpret_cast(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(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(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(&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(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(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(reinterpret_cast(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(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(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(&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 - #include - #include --#include -+#include - - #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::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 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 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::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::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(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()); diff --git a/SPECS/qt5-qtwebsockets.spec b/SPECS/qt5-qtwebsockets.spec index 81b0005..3cc4474 100644 --- a/SPECS/qt5-qtwebsockets.spec +++ b/SPECS/qt5-qtwebsockets.spec @@ -4,8 +4,8 @@ Summary: Qt5 - WebSockets component Name: qt5-%{qt_module} -Version: 5.12.5 -Release: 2%{?dist} +Version: 5.15.2 +Release: 1%{?dist} # See LICENSE.GPL LICENSE.LGPL LGPL_EXCEPTION.txt, for details License: LGPLv2 with exceptions or GPLv3 with exceptions @@ -14,7 +14,6 @@ Url: http://qt-project.org/ Source0: https://download.qt.io/official_releases/qt/%{majmin}/%{version}/submodules/%{qt_module}-everywhere-src-%{version}.tar.xz # Security fixes -Patch100: qtwebsockets-add-public-api-to-set-max-frame-and-message-size.patch # filter qml provides %global __provides_exclude_from ^%{_qt5_archdatadir}/qml/.*\\.so$ @@ -55,8 +54,6 @@ Requires: %{name}%{?_isa} = %{version}-%{release} %prep %setup -q -n %{qt_module}-everywhere-src-%{version} -%patch100 -p1 -b .add-public-api-to-set-max-frame-and-message-size - %build %{qmake_qt5} @@ -124,6 +121,10 @@ popd %endif %changelog +* Mon Apr 05 2021 Jan Grulich - 5.15.2-1 +- 5.15.2 + Resolves: bz#1930060 + * Mon May 11 2020 Jan Grulich - 5.12.5-2 - Add a public api to set max frame and message size Resolves: bz#1815187