summaryrefslogtreecommitdiff
path: root/src/websockets
diff options
context:
space:
mode:
authorSteven Ceuppens <steven.ceuppens@icloud.com>2013-09-29 20:41:29 +0200
committerSteven Ceuppens <steven.ceuppens@icloud.com>2013-09-29 20:50:42 +0200
commit7621610532e94ff1450a09153ab09d38b4cc0db4 (patch)
treea3fde2e453508f658af389fb3b841f697370206f /src/websockets
parent9292fa44f4c8d58aa365b4389be040584a17c893 (diff)
downloadqtwebsockets-7621610532e94ff1450a09153ab09d38b4cc0db4.tar.gz
moved class to separate file
As QWebSocketFrame is quite big, i moved it into its own file, for futher implementation:wq Change-Id: Iadf51323d5e8151c8345057614f4c49f81e626b1 Reviewed-by: Kurt Pattyn <pattyn.kurt@gmail.com>
Diffstat (limited to 'src/websockets')
-rw-r--r--src/websockets/qwebsocketdataprocessor_p.cpp550
-rw-r--r--src/websockets/qwebsocketframe_p.cpp498
-rw-r--r--src/websockets/qwebsocketframe_p.h104
-rw-r--r--src/websockets/websockets.pro6
4 files changed, 607 insertions, 551 deletions
diff --git a/src/websockets/qwebsocketdataprocessor_p.cpp b/src/websockets/qwebsocketdataprocessor_p.cpp
index f5c9c61..f3afdee 100644
--- a/src/websockets/qwebsocketdataprocessor_p.cpp
+++ b/src/websockets/qwebsocketdataprocessor_p.cpp
@@ -32,7 +32,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "qwebsocketdataprocessor_p.h"
#include "qwebsocketprotocol.h"
-#include <QIODevice>
+#include "qwebsocketframe_p.h"
#include <QtEndian>
#include <limits.h>
#include <QTextCodec>
@@ -41,554 +41,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
QT_BEGIN_NAMESPACE
-const quint64 MAX_FRAME_SIZE_IN_BYTES = INT_MAX - 1;
-const quint64 MAX_MESSAGE_SIZE_IN_BYTES = INT_MAX - 1;
-
-/*!
- \class Frame
- The class Frame is responsible for reading, validating and interpreting frames from a websocket.
- It reads data from a QIODevice, validates it against RFC 6455, and parses it into a frame (data, control).
- Whenever an error is detected, the isValid() returns false.
-
- \note The Frame class does not look at valid sequences of frames. It processes frames one at a time.
- \note It is the DataProcessor that takes the sequence into account.
-
- \sa DataProcessor()
- \internal
- */
-class QWebSocketFrame
-{
-public:
- QWebSocketFrame();
- QWebSocketFrame(const QWebSocketFrame &other);
-
- const QWebSocketFrame &operator =(const QWebSocketFrame &other);
-
- QWebSocketProtocol::CloseCode getCloseCode() const;
- QString getCloseReason() const;
- bool isFinalFrame() const;
- bool isControlFrame() const;
- bool isDataFrame() const;
- bool isContinuationFrame() const;
- bool hasMask() const;
- quint32 getMask() const; //returns 0 if no mask
- int getRsv1() const;
- int getRsv2() const;
- int getRsv3() const;
- QWebSocketProtocol::OpCode getOpCode() const;
- QByteArray getPayload() const;
-
- void clear(); //resets all member variables, and invalidates the object
-
- bool isValid() const;
-
- static QWebSocketFrame readFrame(QIODevice *pIoDevice);
-
-private:
- QWebSocketProtocol::CloseCode m_closeCode;
- QString m_closeReason;
- bool m_isFinalFrame;
- quint32 m_mask;
- int m_rsv1; //reserved field 1
- int m_rsv2; //reserved field 2
- int m_rsv3; //reserved field 3
- QWebSocketProtocol::OpCode m_opCode;
-
- quint8 m_length; //length field as read from the header; this is 1 byte, which when 126 or 127, indicates a large payload
- QByteArray m_payload;
-
- bool m_isValid;
-
- 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
- };
-
- void setError(QWebSocketProtocol::CloseCode code, QString closeReason);
- bool checkValidity();
-};
-
-/*!
- \internal
- */
-QWebSocketFrame::QWebSocketFrame() :
- m_closeCode(QWebSocketProtocol::CC_NORMAL),
- m_closeReason(),
- m_isFinalFrame(true),
- m_mask(0),
- m_rsv1(0),
- m_rsv2(0),
- m_rsv3(0),
- m_opCode(QWebSocketProtocol::OC_RESERVED_C),
- m_length(0),
- m_payload(),
- m_isValid(false)
-{
-}
-
-/*!
- \internal
- */
-QWebSocketFrame::QWebSocketFrame(const QWebSocketFrame &other) :
- 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)
-{
-}
-
-/*!
- \internal
- */
-const QWebSocketFrame &QWebSocketFrame::operator =(const QWebSocketFrame &other)
-{
- 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_rsv2;
- m_opCode = other.m_opCode;
- m_length = other.m_length;
- m_payload = other.m_payload;
- m_isValid = other.m_isValid;
-
- return *this;
-}
-
-/*!
- \internal
- */
-QWebSocketProtocol::CloseCode QWebSocketFrame::getCloseCode() const
-{
- return m_closeCode;
-}
-
-/*!
- \internal
- */
-QString QWebSocketFrame::getCloseReason() const
-{
- return m_closeReason;
-}
-
-/*!
- \internal
- */
-bool QWebSocketFrame::isFinalFrame() const
-{
- return m_isFinalFrame;
-}
-
-/*!
- \internal
- */
-bool QWebSocketFrame::isControlFrame() const
-{
- return (m_opCode & 0x08) == 0x08;
-}
-
-/*!
- \internal
- */
-bool QWebSocketFrame::isDataFrame() const
-{
- return !isControlFrame();
-}
-
-/*!
- \internal
- */
-bool QWebSocketFrame::isContinuationFrame() const
-{
- return isDataFrame() && (m_opCode == QWebSocketProtocol::OC_CONTINUE);
-}
-
-/*!
- \internal
- */
-bool QWebSocketFrame::hasMask() const
-{
- return m_mask != 0;
-}
-
-/*!
- \internal
- */
-quint32 QWebSocketFrame::getMask() const
-{
- return m_mask;
-}
-
-/*!
- \internal
- */
-int QWebSocketFrame::getRsv1() const
-{
- return m_rsv1;
-}
-
-/*!
- \internal
- */
-int QWebSocketFrame::getRsv2() const
-{
- return m_rsv2;
-}
-
-/*!
- \internal
- */
-int QWebSocketFrame::getRsv3() const
-{
- return m_rsv3;
-}
-
-/*!
- \internal
- */
-QWebSocketProtocol::OpCode QWebSocketFrame::getOpCode() const
-{
- return m_opCode;
-}
-
-/*!
- \internal
- */
-QByteArray QWebSocketFrame::getPayload() const
-{
- return m_payload;
-}
-
-/*!
- \internal
- */
-void QWebSocketFrame::clear()
-{
- m_closeCode = QWebSocketProtocol::CC_NORMAL;
- m_closeReason.clear();
- m_isFinalFrame = true;
- m_mask = 0;
- m_rsv1 = 0;
- m_rsv2 =0;
- m_rsv3 = 0;
- m_opCode = QWebSocketProtocol::OC_RESERVED_C;
- m_length = 0;
- m_payload.clear();
- m_isValid = false;
-}
-
-/*!
- \internal
- */
-bool QWebSocketFrame::isValid() const
-{
- return m_isValid;
-}
-
-#define WAIT_FOR_MORE_DATA(dataSizeInBytes) { returnState = processingState; processingState = PS_WAIT_FOR_MORE_DATA; dataWaitSize = dataSizeInBytes; }
-
-/*!
- \internal
- */
-QWebSocketFrame QWebSocketFrame::readFrame(QIODevice *pIoDevice)
-{
- bool isDone = false;
- qint64 bytesRead = 0;
- QWebSocketFrame frame;
- quint64 dataWaitSize = 0;
- ProcessingState processingState = PS_READ_HEADER;
- ProcessingState returnState = PS_READ_HEADER;
- bool hasMask = false;
- quint64 payloadLength = 0;
-
- while (!isDone)
- {
- switch (processingState)
- {
- case PS_WAIT_FOR_MORE_DATA:
- {
- bool ok = pIoDevice->waitForReadyRead(5000);
- if (!ok)
- {
- frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::tr("Timeout when reading data from socket."));
- processingState = PS_DISPATCH_RESULT;
- }
- else
- {
- processingState = returnState;
- }
- break;
- }
- case PS_READ_HEADER:
- {
- if (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);
- }
- break;
- }
-
- case PS_READ_PAYLOAD_LENGTH:
- {
- if (pIoDevice->bytesAvailable() >= 2)
- {
- uchar length[2] = {0};
- bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 2);
- if (bytesRead == -1)
- {
- frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::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 (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::CC_PROTOCOL_ERROR, QObject::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);
- }
- break;
- }
-
- case PS_READ_BIG_PAYLOAD_LENGTH:
- {
- if (pIoDevice->bytesAvailable() >= 8)
- {
- uchar length[8] = {0};
- bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 8);
- if (bytesRead < 8)
- {
- frame.setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION, QObject::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
- //TODO: Do we check for that? Now we just strip off the highest bit
- payloadLength = qFromBigEndian<quint64>(length) & ~(1ULL << 63);
- if (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::CC_PROTOCOL_ERROR, QObject::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 (pIoDevice->bytesAvailable() >= 4)
- {
- bytesRead = pIoDevice->read(reinterpret_cast<char *>(&frame.m_mask), sizeof(frame.m_mask));
- if (bytesRead == -1)
- {
- frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::tr("Error while reading from the network: %1.").arg(pIoDevice->errorString()));
- processingState = PS_DISPATCH_RESULT;
- }
- else
- {
- processingState = PS_READ_PAYLOAD;
- }
- }
- else
- {
- WAIT_FOR_MORE_DATA(4);
- }
- break;
- }
-
- case PS_READ_PAYLOAD:
- {
- if (!payloadLength)
- {
- processingState = PS_DISPATCH_RESULT;
- }
- else if (payloadLength > MAX_FRAME_SIZE_IN_BYTES)
- {
- frame.setError(QWebSocketProtocol::CC_TOO_MUCH_DATA, QObject::tr("Maximum framesize exceeded."));
- processingState = PS_DISPATCH_RESULT;
- }
- else
- {
- quint64 bytesAvailable = static_cast<quint64>(pIoDevice->bytesAvailable());
- if (bytesAvailable >= payloadLength)
- {
- frame.m_payload = pIoDevice->read(payloadLength);
- //payloadLength can be safely cast to an integer, as the MAX_FRAME_SIZE_IN_BYTES = MAX_INT
- if (frame.m_payload.length() != static_cast<int>(payloadLength)) //some error occurred; refer to the Qt documentation
- {
- frame.setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION, QObject::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
- {
- WAIT_FOR_MORE_DATA(payloadLength); //if payload is too big, then this will timeout
- }
- }
- break;
- }
-
- case PS_DISPATCH_RESULT:
- {
- processingState = PS_READ_HEADER;
- isDone = true;
- break;
- }
-
- default:
- {
- //should not come here
- qWarning() << "DataProcessor::process: Found invalid state. This should not happen!";
- frame.clear();
- isDone = true;
- break;
- }
- } //end switch
- }
-
- return frame;
-}
-
-/*!
- \internal
- */
-void QWebSocketFrame::setError(QWebSocketProtocol::CloseCode code, QString closeReason)
-{
- clear();
- m_closeCode = code;
- m_closeReason = closeReason;
- m_isValid = false;
-}
-
-/*!
- \internal
- */
-bool QWebSocketFrame::checkValidity()
-{
- if (!isValid())
- {
- if (m_rsv1 || m_rsv2 || m_rsv3)
- {
- setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Rsv field is non-zero"));
- }
- else if (QWebSocketProtocol::isOpCodeReserved(m_opCode))
- {
- setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Used reserved opcode"));
- }
- else if (isControlFrame())
- {
- if (m_length > 125)
- {
- setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Controle frame is larger than 125 bytes"));
- }
- else if (!m_isFinalFrame)
- {
- setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Controle frames cannot be fragmented"));
- }
- else
- {
- m_isValid = true;
- }
- }
- else
- {
- m_isValid = true;
- }
- }
- return m_isValid;
-}
-
/*!
\internal
*/
diff --git a/src/websockets/qwebsocketframe_p.cpp b/src/websockets/qwebsocketframe_p.cpp
new file mode 100644
index 0000000..b4d61e7
--- /dev/null
+++ b/src/websockets/qwebsocketframe_p.cpp
@@ -0,0 +1,498 @@
+/*
+QWebSockets implements the WebSocket protocol as defined in RFC 6455.
+Copyright (C) 2013 Kurt Pattyn (pattyn.kurt@gmail.com)
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "qwebsocketframe_p.h"
+
+#include <QtEndian>
+#include <QDebug>
+
+/*!
+ \internal
+ */
+QWebSocketFrame::QWebSocketFrame() :
+ m_closeCode(QWebSocketProtocol::CC_NORMAL),
+ m_closeReason(),
+ m_isFinalFrame(true),
+ m_mask(0),
+ m_rsv1(0),
+ m_rsv2(0),
+ m_rsv3(0),
+ m_opCode(QWebSocketProtocol::OC_RESERVED_C),
+ m_length(0),
+ m_payload(),
+ m_isValid(false)
+{
+}
+
+/*!
+ \internal
+ */
+QWebSocketFrame::QWebSocketFrame(const QWebSocketFrame &other) :
+ 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)
+{
+}
+
+/*!
+ \internal
+ */
+const QWebSocketFrame &QWebSocketFrame::operator =(const QWebSocketFrame &other)
+{
+ 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_rsv2;
+ m_opCode = other.m_opCode;
+ m_length = other.m_length;
+ m_payload = other.m_payload;
+ m_isValid = other.m_isValid;
+
+ return *this;
+}
+
+/*!
+ \internal
+ */
+QWebSocketProtocol::CloseCode QWebSocketFrame::getCloseCode() const
+{
+ return m_closeCode;
+}
+
+/*!
+ \internal
+ */
+QString QWebSocketFrame::getCloseReason() const
+{
+ return m_closeReason;
+}
+
+/*!
+ \internal
+ */
+bool QWebSocketFrame::isFinalFrame() const
+{
+ return m_isFinalFrame;
+}
+
+/*!
+ \internal
+ */
+bool QWebSocketFrame::isControlFrame() const
+{
+ return (m_opCode & 0x08) == 0x08;
+}
+
+/*!
+ \internal
+ */
+bool QWebSocketFrame::isDataFrame() const
+{
+ return !isControlFrame();
+}
+
+/*!
+ \internal
+ */
+bool QWebSocketFrame::isContinuationFrame() const
+{
+ return isDataFrame() && (m_opCode == QWebSocketProtocol::OC_CONTINUE);
+}
+
+/*!
+ \internal
+ */
+bool QWebSocketFrame::hasMask() const
+{
+ return m_mask != 0;
+}
+
+/*!
+ \internal
+ */
+quint32 QWebSocketFrame::getMask() const
+{
+ return m_mask;
+}
+
+/*!
+ \internal
+ */
+int QWebSocketFrame::getRsv1() const
+{
+ return m_rsv1;
+}
+
+/*!
+ \internal
+ */
+int QWebSocketFrame::getRsv2() const
+{
+ return m_rsv2;
+}
+
+/*!
+ \internal
+ */
+int QWebSocketFrame::getRsv3() const
+{
+ return m_rsv3;
+}
+
+/*!
+ \internal
+ */
+QWebSocketProtocol::OpCode QWebSocketFrame::getOpCode() const
+{
+ return m_opCode;
+}
+
+/*!
+ \internal
+ */
+QByteArray QWebSocketFrame::getPayload() const
+{
+ return m_payload;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketFrame::clear()
+{
+ m_closeCode = QWebSocketProtocol::CC_NORMAL;
+ m_closeReason.clear();
+ m_isFinalFrame = true;
+ m_mask = 0;
+ m_rsv1 = 0;
+ m_rsv2 =0;
+ m_rsv3 = 0;
+ m_opCode = QWebSocketProtocol::OC_RESERVED_C;
+ m_length = 0;
+ m_payload.clear();
+ m_isValid = false;
+}
+
+/*!
+ \internal
+ */
+bool QWebSocketFrame::isValid() const
+{
+ return m_isValid;
+}
+
+#define WAIT_FOR_MORE_DATA(dataSizeInBytes) { returnState = processingState; processingState = PS_WAIT_FOR_MORE_DATA; dataWaitSize = dataSizeInBytes; }
+
+/*!
+ \internal
+ */
+QWebSocketFrame QWebSocketFrame::readFrame(QIODevice *pIoDevice)
+{
+ bool isDone = false;
+ qint64 bytesRead = 0;
+ QWebSocketFrame frame;
+ quint64 dataWaitSize = 0;
+ ProcessingState processingState = PS_READ_HEADER;
+ ProcessingState returnState = PS_READ_HEADER;
+ bool hasMask = false;
+ quint64 payloadLength = 0;
+
+ while (!isDone)
+ {
+ switch (processingState)
+ {
+ case PS_WAIT_FOR_MORE_DATA:
+ {
+ bool ok = pIoDevice->waitForReadyRead(5000);
+ if (!ok)
+ {
+ frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::tr("Timeout when reading data from socket."));
+ processingState = PS_DISPATCH_RESULT;
+ }
+ else
+ {
+ processingState = returnState;
+ }
+ break;
+ }
+ case PS_READ_HEADER:
+ {
+ if (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);
+ }
+ break;
+ }
+
+ case PS_READ_PAYLOAD_LENGTH:
+ {
+ if (pIoDevice->bytesAvailable() >= 2)
+ {
+ uchar length[2] = {0};
+ bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 2);
+ if (bytesRead == -1)
+ {
+ frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::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 (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::CC_PROTOCOL_ERROR, QObject::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);
+ }
+ break;
+ }
+
+ case PS_READ_BIG_PAYLOAD_LENGTH:
+ {
+ if (pIoDevice->bytesAvailable() >= 8)
+ {
+ uchar length[8] = {0};
+ bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 8);
+ if (bytesRead < 8)
+ {
+ frame.setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION, QObject::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
+ //TODO: Do we check for that? Now we just strip off the highest bit
+ payloadLength = qFromBigEndian<quint64>(length) & ~(1ULL << 63);
+ if (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::CC_PROTOCOL_ERROR, QObject::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 (pIoDevice->bytesAvailable() >= 4)
+ {
+ bytesRead = pIoDevice->read(reinterpret_cast<char *>(&frame.m_mask), sizeof(frame.m_mask));
+ if (bytesRead == -1)
+ {
+ frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::tr("Error while reading from the network: %1.").arg(pIoDevice->errorString()));
+ processingState = PS_DISPATCH_RESULT;
+ }
+ else
+ {
+ processingState = PS_READ_PAYLOAD;
+ }
+ }
+ else
+ {
+ WAIT_FOR_MORE_DATA(4);
+ }
+ break;
+ }
+
+ case PS_READ_PAYLOAD:
+ {
+ if (!payloadLength)
+ {
+ processingState = PS_DISPATCH_RESULT;
+ }
+ else if (payloadLength > MAX_FRAME_SIZE_IN_BYTES)
+ {
+ frame.setError(QWebSocketProtocol::CC_TOO_MUCH_DATA, QObject::tr("Maximum framesize exceeded."));
+ processingState = PS_DISPATCH_RESULT;
+ }
+ else
+ {
+ quint64 bytesAvailable = static_cast<quint64>(pIoDevice->bytesAvailable());
+ if (bytesAvailable >= payloadLength)
+ {
+ frame.m_payload = pIoDevice->read(payloadLength);
+ //payloadLength can be safely cast to an integer, as the MAX_FRAME_SIZE_IN_BYTES = MAX_INT
+ if (frame.m_payload.length() != static_cast<int>(payloadLength)) //some error occurred; refer to the Qt documentation
+ {
+ frame.setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION, QObject::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
+ {
+ WAIT_FOR_MORE_DATA(payloadLength); //if payload is too big, then this will timeout
+ }
+ }
+ break;
+ }
+
+ case PS_DISPATCH_RESULT:
+ {
+ processingState = PS_READ_HEADER;
+ isDone = true;
+ break;
+ }
+
+ default:
+ {
+ //should not come here
+ qWarning() << "DataProcessor::process: Found invalid state. This should not happen!";
+ frame.clear();
+ isDone = true;
+ break;
+ }
+ } //end switch
+ }
+
+ return frame;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketFrame::setError(QWebSocketProtocol::CloseCode code, QString closeReason)
+{
+ clear();
+ m_closeCode = code;
+ m_closeReason = closeReason;
+ m_isValid = false;
+}
+
+/*!
+ \internal
+ */
+bool QWebSocketFrame::checkValidity()
+{
+ if (!isValid())
+ {
+ if (m_rsv1 || m_rsv2 || m_rsv3)
+ {
+ setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Rsv field is non-zero"));
+ }
+ else if (QWebSocketProtocol::isOpCodeReserved(m_opCode))
+ {
+ setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Used reserved opcode"));
+ }
+ else if (isControlFrame())
+ {
+ if (m_length > 125)
+ {
+ setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Controle frame is larger than 125 bytes"));
+ }
+ else if (!m_isFinalFrame)
+ {
+ setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Controle frames cannot be fragmented"));
+ }
+ else
+ {
+ m_isValid = true;
+ }
+ }
+ else
+ {
+ m_isValid = true;
+ }
+ }
+ return m_isValid;
+}
diff --git a/src/websockets/qwebsocketframe_p.h b/src/websockets/qwebsocketframe_p.h
new file mode 100644
index 0000000..434fead
--- /dev/null
+++ b/src/websockets/qwebsocketframe_p.h
@@ -0,0 +1,104 @@
+/*
+QWebSockets implements the WebSocket protocol as defined in RFC 6455.
+Copyright (C) 2013 Kurt Pattyn (pattyn.kurt@gmail.com)
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef QWEBSOCKETFRAME_P_H
+#define QWEBSOCKETFRAME_P_H
+
+#include "qwebsocketprotocol.h"
+
+#include <QIODevice>
+
+QT_BEGIN_NAMESPACE
+
+const quint64 MAX_FRAME_SIZE_IN_BYTES = INT_MAX - 1;
+const quint64 MAX_MESSAGE_SIZE_IN_BYTES = INT_MAX - 1;
+
+/*!
+ \class Frame
+ The class Frame is responsible for reading, validating and interpreting frames from a websocket.
+ It reads data from a QIODevice, validates it against RFC 6455, and parses it into a frame (data, control).
+ Whenever an error is detected, the isValid() returns false.
+
+ \note The Frame class does not look at valid sequences of frames. It processes frames one at a time.
+ \note It is the DataProcessor that takes the sequence into account.
+
+ \sa DataProcessor()
+ \internal
+ */
+class QWebSocketFrame
+{
+public:
+ QWebSocketFrame();
+ QWebSocketFrame(const QWebSocketFrame &other);
+
+ const QWebSocketFrame &operator =(const QWebSocketFrame &other);
+
+ QWebSocketProtocol::CloseCode getCloseCode() const;
+ QString getCloseReason() const;
+ bool isFinalFrame() const;
+ bool isControlFrame() const;
+ bool isDataFrame() const;
+ bool isContinuationFrame() const;
+ bool hasMask() const;
+ quint32 getMask() const; //returns 0 if no mask
+ int getRsv1() const;
+ int getRsv2() const;
+ int getRsv3() const;
+ QWebSocketProtocol::OpCode getOpCode() const;
+ QByteArray getPayload() const;
+
+ void clear(); //resets all member variables, and invalidates the object
+
+ bool isValid() const;
+
+ static QWebSocketFrame readFrame(QIODevice *pIoDevice);
+
+private:
+ QWebSocketProtocol::CloseCode m_closeCode;
+ QString m_closeReason;
+ bool m_isFinalFrame;
+ quint32 m_mask;
+ int m_rsv1; //reserved field 1
+ int m_rsv2; //reserved field 2
+ int m_rsv3; //reserved field 3
+ QWebSocketProtocol::OpCode m_opCode;
+
+ quint8 m_length; //length field as read from the header; this is 1 byte, which when 126 or 127, indicates a large payload
+ QByteArray m_payload;
+
+ bool m_isValid;
+
+ 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
+ };
+
+ void setError(QWebSocketProtocol::CloseCode code, QString closeReason);
+ bool checkValidity();
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBSOCKETFRAME_P_H
diff --git a/src/websockets/websockets.pro b/src/websockets/websockets.pro
index e7d6bbc..5ba40c1 100644
--- a/src/websockets/websockets.pro
+++ b/src/websockets/websockets.pro
@@ -23,7 +23,8 @@ PRIVATE_HEADERS += \
$$PWD/qwebsockethandshakerequest_p.h \
$$PWD/qwebsockethandshakeresponse_p.h \
$$PWD/qwebsocketdataprocessor_p.h \
- $$PWD/qwebsocketcorsauthenticator_p.h
+ $$PWD/qwebsocketcorsauthenticator_p.h \
+ $$PWD/qwebsocketframe_p.h
SOURCES += \
$$PWD/qwebsocket.cpp \
@@ -34,6 +35,7 @@ SOURCES += \
$$PWD/qwebsockethandshakerequest_p.cpp \
$$PWD/qwebsockethandshakeresponse_p.cpp \
$$PWD/qwebsocketdataprocessor_p.cpp \
- $$PWD/qwebsocketcorsauthenticator.cpp
+ $$PWD/qwebsocketcorsauthenticator.cpp \
+ $$PWD/qwebsocketframe_p.cpp
HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS