2 QWebSockets implements the WebSocket protocol as defined in RFC 6455.
3 Copyright (C) 2013 Kurt Pattyn (pattyn.kurt@gmail.com)
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 The class DataProcessor is responsible for reading, validating and interpreting data from a websocket.
22 It reads data from a QIODevice, validates it against RFC 6455, and parses it into frames (data, control).
23 It emits signals that correspond to the type of the frame: textFrameReceived(), binaryFrameReceived(),
24 textMessageReceived(), binaryMessageReceived(), pingReceived(), pongReceived() and closeReceived().
25 Whenever an error is detected, the errorEncountered() signal is emitted.
26 DataProcessor also checks if a frame is allowed in a sequence of frames (e.g. a continuation frame cannot follow a final frame).
27 This class is an internal class used by QWebSocketInternal for data processing and validation.
33 #include "dataprocessor_p.h"
34 #include "qwebsocketprotocol.h"
39 #include <QTextDecoder>
44 const quint64 MAX_FRAME_SIZE_IN_BYTES = INT_MAX - 1;
45 const quint64 MAX_MESSAGE_SIZE_IN_BYTES = INT_MAX - 1;
49 The class Frame is responsible for reading, validating and interpreting frames from a websocket.
50 It reads data from a QIODevice, validates it against RFC 6455, and parses it into a frame (data, control).
51 Whenever an error is detected, the isValid() returns false.
53 \note The Frame class does not look at valid sequences of frames. It processes frames one at a time.
54 \note It is the DataProcessor that takes the sequence into account.
63 Frame(const Frame &other);
65 const Frame &operator =(const Frame &other);
67 QWebSocketProtocol::CloseCode getCloseCode() const;
68 QString getCloseReason() const;
69 bool isFinalFrame() const;
70 bool isControlFrame() const;
71 bool isDataFrame() const;
72 bool isContinuationFrame() const;
74 quint32 getMask() const; //returns 0 if no mask
78 QWebSocketProtocol::OpCode getOpCode() const;
79 QByteArray getPayload() const;
81 void clear(); //resets all member variables, and invalidates the object
85 static Frame readFrame(QIODevice *pIoDevice);
88 QWebSocketProtocol::CloseCode m_closeCode;
89 QString m_closeReason;
92 int m_rsv1; //reserved field 1
93 int m_rsv2; //reserved field 2
94 int m_rsv3; //reserved field 3
95 QWebSocketProtocol::OpCode m_opCode;
97 quint8 m_length; //length field as read from the header; this is 1 byte, which when 126 or 127, indicates a large payload
105 PS_READ_PAYLOAD_LENGTH,
106 PS_READ_BIG_PAYLOAD_LENGTH,
110 PS_WAIT_FOR_MORE_DATA
113 void setError(QWebSocketProtocol::CloseCode code, QString closeReason);
114 bool checkValidity();
121 m_closeCode(QWebSocketProtocol::CC_NORMAL),
123 m_isFinalFrame(true),
128 m_opCode(QWebSocketProtocol::OC_RESERVED_C),
138 Frame::Frame(const Frame &other) :
139 m_closeCode(other.m_closeCode),
140 m_closeReason(other.m_closeReason),
141 m_isFinalFrame(other.m_isFinalFrame),
142 m_mask(other.m_mask),
143 m_rsv1(other.m_rsv1),
144 m_rsv2(other.m_rsv2),
145 m_rsv3(other.m_rsv3),
146 m_opCode(other.m_opCode),
147 m_length(other.m_length),
148 m_payload(other.m_payload),
149 m_isValid(other.m_isValid)
156 const Frame &Frame::operator =(const Frame &other)
158 m_closeCode = other.m_closeCode;
159 m_closeReason = other.m_closeReason;
160 m_isFinalFrame = other.m_isFinalFrame;
161 m_mask = other.m_mask;
162 m_rsv1 = other.m_rsv1;
163 m_rsv2 = other.m_rsv2;
164 m_rsv3 = other.m_rsv2;
165 m_opCode = other.m_opCode;
166 m_length = other.m_length;
167 m_payload = other.m_payload;
168 m_isValid = other.m_isValid;
176 QWebSocketProtocol::CloseCode Frame::getCloseCode() const
184 QString Frame::getCloseReason() const
186 return m_closeReason;
192 bool Frame::isFinalFrame() const
194 return m_isFinalFrame;
200 bool Frame::isControlFrame() const
202 return (m_opCode & 0x08) == 0x08;
208 bool Frame::isDataFrame() const
210 return !isControlFrame();
216 bool Frame::isContinuationFrame() const
218 return isDataFrame() && (m_opCode == QWebSocketProtocol::OC_CONTINUE);
224 bool Frame::hasMask() const
232 quint32 Frame::getMask() const
240 int Frame::getRsv1() const
248 int Frame::getRsv2() const
256 int Frame::getRsv3() const
264 QWebSocketProtocol::OpCode Frame::getOpCode() const
272 QByteArray Frame::getPayload() const
282 m_closeCode = QWebSocketProtocol::CC_NORMAL;
283 m_closeReason.clear();
284 m_isFinalFrame = true;
289 m_opCode = QWebSocketProtocol::OC_RESERVED_C;
298 bool Frame::isValid() const
303 #define WAIT_FOR_MORE_DATA(dataSizeInBytes) { returnState = processingState; processingState = PS_WAIT_FOR_MORE_DATA; dataWaitSize = dataSizeInBytes; }
308 Frame Frame::readFrame(QIODevice *pIoDevice)
311 qint64 bytesRead = 0;
313 quint64 dataWaitSize = 0;
314 ProcessingState processingState = PS_READ_HEADER;
315 ProcessingState returnState = PS_READ_HEADER;
316 bool hasMask = false;
317 quint64 payloadLength = 0;
321 switch (processingState)
323 case PS_WAIT_FOR_MORE_DATA:
325 bool ok = pIoDevice->waitForReadyRead(5000);
328 frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::tr("Timeout when reading data from socket."));
329 processingState = PS_DISPATCH_RESULT;
333 processingState = returnState;
339 if (pIoDevice->bytesAvailable() >= 2)
341 //FIN, RSV1-3, Opcode
342 char header[2] = {0};
343 bytesRead = pIoDevice->read(header, 2);
344 frame.m_isFinalFrame = (header[0] & 0x80) != 0;
345 frame.m_rsv1 = (header[0] & 0x40);
346 frame.m_rsv2 = (header[0] & 0x20);
347 frame.m_rsv3 = (header[0] & 0x10);
348 frame.m_opCode = static_cast<QWebSocketProtocol::OpCode>(header[0] & 0x0F);
350 //Mask, PayloadLength
351 hasMask = (header[1] & 0x80) != 0;
352 frame.m_length = (header[1] & 0x7F);
354 switch (frame.m_length)
358 processingState = PS_READ_PAYLOAD_LENGTH;
363 processingState = PS_READ_BIG_PAYLOAD_LENGTH;
368 payloadLength = frame.m_length;
369 processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
373 if (!frame.checkValidity())
375 processingState = PS_DISPATCH_RESULT;
380 WAIT_FOR_MORE_DATA(2);
385 case PS_READ_PAYLOAD_LENGTH:
387 if (pIoDevice->bytesAvailable() >= 2)
389 uchar length[2] = {0};
390 bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 2);
393 frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::tr("Error occurred while reading from the network: %1").arg(pIoDevice->errorString()));
394 processingState = PS_DISPATCH_RESULT;
398 payloadLength = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(length));
399 if (payloadLength < 126)
401 //see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
402 //"in all cases, the minimal number of bytes MUST be used to encode
403 //the length, for example, the length of a 124-byte-long string
404 //can't be encoded as the sequence 126, 0, 124"
405 frame.setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Lengths smaller than 126 must be expressed as one byte."));
406 processingState = PS_DISPATCH_RESULT;
410 processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
416 WAIT_FOR_MORE_DATA(2);
421 case PS_READ_BIG_PAYLOAD_LENGTH:
423 if (pIoDevice->bytesAvailable() >= 8)
425 uchar length[8] = {0};
426 bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 8);
429 frame.setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION, QObject::tr("Something went wrong during reading from the network."));
430 processingState = PS_DISPATCH_RESULT;
434 //Most significant bit must be set to 0 as per http://tools.ietf.org/html/rfc6455#section-5.2
435 //TODO: Do we check for that? Now we just strip off the highest bit
436 payloadLength = qFromBigEndian<quint64>(length) & ~(1ULL << 63);
437 if (payloadLength <= 0xFFFFu)
439 //see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
440 //"in all cases, the minimal number of bytes MUST be used to encode
441 //the length, for example, the length of a 124-byte-long string
442 //can't be encoded as the sequence 126, 0, 124"
443 frame.setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Lengths smaller than 65536 (2^16) must be expressed as 2 bytes."));
444 processingState = PS_DISPATCH_RESULT;
448 processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
454 WAIT_FOR_MORE_DATA(8);
462 if (pIoDevice->bytesAvailable() >= 4)
464 bytesRead = pIoDevice->read(reinterpret_cast<char *>(&frame.m_mask), sizeof(frame.m_mask));
467 frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::tr("Error while reading from the network: %1.").arg(pIoDevice->errorString()));
468 processingState = PS_DISPATCH_RESULT;
472 processingState = PS_READ_PAYLOAD;
477 WAIT_FOR_MORE_DATA(4);
482 case PS_READ_PAYLOAD:
486 processingState = PS_DISPATCH_RESULT;
488 else if (payloadLength > MAX_FRAME_SIZE_IN_BYTES)
490 frame.setError(QWebSocketProtocol::CC_TOO_MUCH_DATA, QObject::tr("Maximum framesize exceeded."));
491 processingState = PS_DISPATCH_RESULT;
495 quint64 bytesAvailable = static_cast<quint64>(pIoDevice->bytesAvailable());
496 if (bytesAvailable >= payloadLength)
498 frame.m_payload = pIoDevice->read(payloadLength);
499 //payloadLength can be safely cast to an integer, as the MAX_FRAME_SIZE_IN_BYTES = MAX_INT
500 if (frame.m_payload.length() != static_cast<int>(payloadLength)) //some error occurred; refer to the Qt documentation
502 frame.setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION, QObject::tr("Some serious error occurred while reading from the network."));
503 processingState = PS_DISPATCH_RESULT;
509 QWebSocketProtocol::mask(&frame.m_payload, frame.m_mask);
511 processingState = PS_DISPATCH_RESULT;
516 WAIT_FOR_MORE_DATA(payloadLength); //if payload is too big, then this will timeout
522 case PS_DISPATCH_RESULT:
524 processingState = PS_READ_HEADER;
531 //should not come here
532 qWarning() << "DataProcessor::process: Found invalid state. This should not happen!";
546 void Frame::setError(QWebSocketProtocol::CloseCode code, QString closeReason)
550 m_closeReason = closeReason;
557 bool Frame::checkValidity()
561 if (m_rsv1 || m_rsv2 || m_rsv3)
563 setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Rsv field is non-zero"));
565 else if (QWebSocketProtocol::isOpCodeReserved(m_opCode))
567 setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Used reserved opcode"));
569 else if (isControlFrame())
573 setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Controle frame is larger than 125 bytes"));
575 else if (!m_isFinalFrame)
577 setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Controle frames cannot be fragmented"));
595 DataProcessor::DataProcessor(QObject *parent) :
597 m_processingState(PS_READ_HEADER),
598 m_isFinalFrame(false),
599 m_isFragmented(false),
600 m_opCode(QWebSocketProtocol::OC_CLOSE),
601 m_isControlFrame(false),
607 m_pConverterState(0),
608 m_pTextCodec(QTextCodec::codecForName("UTF-8"))
616 DataProcessor::~DataProcessor()
619 if (m_pConverterState)
621 delete m_pConverterState;
622 m_pConverterState = 0;
629 quint64 DataProcessor::maxMessageSize()
631 return MAX_MESSAGE_SIZE_IN_BYTES;
637 quint64 DataProcessor::maxFrameSize()
639 return MAX_FRAME_SIZE_IN_BYTES;
645 void DataProcessor::process(QIODevice *pIoDevice)
651 Frame frame = Frame::readFrame(pIoDevice);
654 if (frame.isControlFrame())
656 isDone = processControlFrame(frame);
658 else //we have a dataframe; opcode can be OC_CONTINUE, OC_TEXT or OC_BINARY
660 if (!m_isFragmented && frame.isContinuationFrame())
663 Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR, tr("Received Continuation frame, while there is nothing to continue."));
666 if (m_isFragmented && frame.isDataFrame() && !frame.isContinuationFrame())
669 Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR, tr("All data frames after the initial data frame must have opcode 0 (continuation)."));
672 if (!frame.isContinuationFrame())
674 m_opCode = frame.getOpCode();
675 m_isFragmented = !frame.isFinalFrame();
677 quint64 messageLength = (quint64)(m_opCode == QWebSocketProtocol::OC_TEXT) ? m_textMessage.length() : m_binaryMessage.length();
678 if ((messageLength + quint64(frame.getPayload().length())) > MAX_MESSAGE_SIZE_IN_BYTES)
681 Q_EMIT errorEncountered(QWebSocketProtocol::CC_TOO_MUCH_DATA, tr("Received message is too big."));
685 if (m_opCode == QWebSocketProtocol::OC_TEXT)
687 QString frameTxt = m_pTextCodec->toUnicode(frame.getPayload().constData(), frame.getPayload().size(), m_pConverterState);
688 bool failed = (m_pConverterState->invalidChars != 0) || (frame.isFinalFrame() && (m_pConverterState->remainingChars != 0));
692 Q_EMIT errorEncountered(QWebSocketProtocol::CC_WRONG_DATATYPE, tr("Invalid UTF-8 code encountered."));
697 m_textMessage.append(frameTxt);
698 Q_EMIT textFrameReceived(frameTxt, frame.isFinalFrame());
703 m_binaryMessage.append(frame.getPayload());
704 Q_EMIT binaryFrameReceived(frame.getPayload(), frame.isFinalFrame());
707 if (frame.isFinalFrame())
709 if (m_opCode == QWebSocketProtocol::OC_TEXT)
711 Q_EMIT textMessageReceived(m_textMessage);
715 Q_EMIT binaryMessageReceived(m_binaryMessage);
724 Q_EMIT errorEncountered(frame.getCloseCode(), frame.getCloseReason());
734 void DataProcessor::clear()
736 m_processingState = PS_READ_HEADER;
737 m_isFinalFrame = false;
738 m_isFragmented = false;
739 m_opCode = QWebSocketProtocol::OC_CLOSE;
742 m_binaryMessage.clear();
743 m_textMessage.clear();
745 if (m_pConverterState)
747 if ((m_pConverterState->remainingChars != 0) || (m_pConverterState->invalidChars != 0))
749 delete m_pConverterState;
750 m_pConverterState = 0;
753 if (!m_pConverterState)
755 m_pConverterState = new QTextCodec::ConverterState(QTextCodec::ConvertInvalidToNull | QTextCodec::IgnoreHeader);
762 bool DataProcessor::processControlFrame(const Frame &frame)
764 bool mustStopProcessing = false;
765 switch (frame.getOpCode())
767 case QWebSocketProtocol::OC_PING:
769 Q_EMIT pingReceived(frame.getPayload());
772 case QWebSocketProtocol::OC_PONG:
774 Q_EMIT pongReceived(frame.getPayload());
777 case QWebSocketProtocol::OC_CLOSE:
779 quint16 closeCode = QWebSocketProtocol::CC_NORMAL;
781 QByteArray payload = frame.getPayload();
782 if (payload.size() == 1)
784 closeCode = QWebSocketProtocol::CC_PROTOCOL_ERROR;
785 closeReason = tr("Payload of close frame is too small.");
787 else if (payload.size() > 1) //close frame can have a close code and reason
789 closeCode = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(payload.constData()));
790 if (!QWebSocketProtocol::isCloseCodeValid(closeCode))
792 closeCode = QWebSocketProtocol::CC_PROTOCOL_ERROR;
793 closeReason = tr("Invalid close code %1 detected.").arg(closeCode);
797 if (payload.size() > 2)
799 QTextCodec *tc = QTextCodec::codecForName("UTF-8");
800 QTextCodec::ConverterState state(QTextCodec::ConvertInvalidToNull);
801 closeReason = tc->toUnicode(payload.constData() + 2, payload.size() - 2, &state);
802 bool failed = (state.invalidChars != 0) || (state.remainingChars != 0);
805 closeCode = QWebSocketProtocol::CC_WRONG_DATATYPE;
806 closeReason = tr("Invalid UTF-8 code encountered.");
811 mustStopProcessing = true;
812 Q_EMIT closeReceived(static_cast<QWebSocketProtocol::CloseCode>(closeCode), closeReason);
815 case QWebSocketProtocol::OC_CONTINUE:
816 case QWebSocketProtocol::OC_BINARY:
817 case QWebSocketProtocol::OC_TEXT:
818 case QWebSocketProtocol::OC_RESERVED_3:
819 case QWebSocketProtocol::OC_RESERVED_4:
820 case QWebSocketProtocol::OC_RESERVED_5:
821 case QWebSocketProtocol::OC_RESERVED_6:
822 case QWebSocketProtocol::OC_RESERVED_7:
823 case QWebSocketProtocol::OC_RESERVED_C:
824 case QWebSocketProtocol::OC_RESERVED_B:
825 case QWebSocketProtocol::OC_RESERVED_D:
826 case QWebSocketProtocol::OC_RESERVED_E:
827 case QWebSocketProtocol::OC_RESERVED_F:
830 //case added to make C++ compiler happy
835 qDebug() << "DataProcessor::processControlFrame: Invalid opcode detected:" << static_cast<int>(frame.getOpCode());
840 return mustStopProcessing;