1 /****************************************************************************
3 ** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtWebSockets module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
43 \class QWebSocketFrame
44 The class QWebSocketFrame is responsible for reading, validating and
45 interpreting frames from a websocket.
46 It reads data from a QIODevice, validates it against RFC 6455, and parses it into a
47 frame (data, control).
48 Whenever an error is detected, isValid() returns false.
50 \note The QWebSocketFrame class does not look at valid sequences of frames.
51 It processes frames one at a time.
52 \note It is the QWebSocketDataProcessor that takes the sequence into account.
54 \sa QWebSocketDataProcessor
58 #include "qwebsocketframe_p.h"
59 #include "qwebsocketprotocol_p.h"
61 #include <QtCore/QtEndian>
62 #include <QtCore/QDebug>
69 QWebSocketFrame::QWebSocketFrame() :
70 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
77 m_opCode(QWebSocketProtocol::OpCodeReservedC),
87 QWebSocketFrame::QWebSocketFrame(const QWebSocketFrame &other) :
88 m_closeCode(other.m_closeCode),
89 m_closeReason(other.m_closeReason),
90 m_isFinalFrame(other.m_isFinalFrame),
95 m_opCode(other.m_opCode),
96 m_length(other.m_length),
97 m_payload(other.m_payload),
98 m_isValid(other.m_isValid)
105 QWebSocketFrame &QWebSocketFrame::operator =(const QWebSocketFrame &other)
107 m_closeCode = other.m_closeCode;
108 m_closeReason = other.m_closeReason;
109 m_isFinalFrame = other.m_isFinalFrame;
110 m_mask = other.m_mask;
111 m_rsv1 = other.m_rsv1;
112 m_rsv2 = other.m_rsv2;
113 m_rsv3 = other.m_rsv2;
114 m_opCode = other.m_opCode;
115 m_length = other.m_length;
116 m_payload = other.m_payload;
117 m_isValid = other.m_isValid;
122 #ifdef Q_COMPILER_RVALUE_REFS
126 QWebSocketFrame::QWebSocketFrame(QWebSocketFrame &&other) :
127 m_closeCode(qMove(other.m_closeCode)),
128 m_closeReason(qMove(other.m_closeReason)),
129 m_isFinalFrame(qMove(other.m_isFinalFrame)),
130 m_mask(qMove(other.m_mask)),
131 m_rsv1(qMove(other.m_rsv1)),
132 m_rsv2(qMove(other.m_rsv2)),
133 m_rsv3(qMove(other.m_rsv3)),
134 m_opCode(qMove(other.m_opCode)),
135 m_length(qMove(other.m_length)),
136 m_payload(qMove(other.m_payload)),
137 m_isValid(qMove(other.m_isValid))
144 QWebSocketFrame &QWebSocketFrame::operator =(QWebSocketFrame &&other)
146 qSwap(m_closeCode, other.m_closeCode);
147 qSwap(m_closeReason, other.m_closeReason);
148 qSwap(m_isFinalFrame, other.m_isFinalFrame);
149 qSwap(m_mask, other.m_mask);
150 qSwap(m_rsv1, other.m_rsv1);
151 qSwap(m_rsv2, other.m_rsv2);
152 qSwap(m_rsv3, other.m_rsv3);
153 qSwap(m_opCode, other.m_opCode);
154 qSwap(m_length, other.m_length);
155 qSwap(m_payload, other.m_payload);
156 qSwap(m_isValid, other.m_isValid);
166 void QWebSocketFrame::swap(QWebSocketFrame &other)
168 if (&other != this) {
169 qSwap(m_closeCode, other.m_closeCode);
170 qSwap(m_closeReason, other.m_closeReason);
171 qSwap(m_isFinalFrame, other.m_isFinalFrame);
172 qSwap(m_mask, other.m_mask);
173 qSwap(m_rsv1, other.m_rsv1);
174 qSwap(m_rsv2, other.m_rsv2);
175 qSwap(m_rsv3, other.m_rsv3);
176 qSwap(m_opCode, other.m_opCode);
177 qSwap(m_length, other.m_length);
178 qSwap(m_payload, other.m_payload);
179 qSwap(m_isValid, other.m_isValid);
186 QWebSocketProtocol::CloseCode QWebSocketFrame::closeCode() const
194 QString QWebSocketFrame::closeReason() const
196 return m_closeReason;
202 bool QWebSocketFrame::isFinalFrame() const
204 return m_isFinalFrame;
210 bool QWebSocketFrame::isControlFrame() const
212 return (m_opCode & 0x08) == 0x08;
218 bool QWebSocketFrame::isDataFrame() const
220 return !isControlFrame();
226 bool QWebSocketFrame::isContinuationFrame() const
228 return isDataFrame() && (m_opCode == QWebSocketProtocol::OpCodeContinue);
234 bool QWebSocketFrame::hasMask() const
242 quint32 QWebSocketFrame::mask() const
250 int QWebSocketFrame::rsv1() const
258 int QWebSocketFrame::rsv2() const
266 int QWebSocketFrame::rsv3() const
274 QWebSocketProtocol::OpCode QWebSocketFrame::opCode() const
282 QByteArray QWebSocketFrame::payload() const
288 Resets all member variables, and invalidates the object.
292 void QWebSocketFrame::clear()
294 m_closeCode = QWebSocketProtocol::CloseCodeNormal;
295 m_closeReason.clear();
296 m_isFinalFrame = true;
301 m_opCode = QWebSocketProtocol::OpCodeReservedC;
310 bool QWebSocketFrame::isValid() const
315 #define WAIT_FOR_MORE_DATA(dataSizeInBytes) \
316 { returnState = processingState; \
317 processingState = PS_WAIT_FOR_MORE_DATA; dataWaitSize = dataSizeInBytes; }
322 QWebSocketFrame QWebSocketFrame::readFrame(QIODevice *pIoDevice)
325 qint64 bytesRead = 0;
326 QWebSocketFrame frame;
327 quint64 dataWaitSize = 0;
328 Q_UNUSED(dataWaitSize); // value is used in MACRO, Q_UNUSED to avoid compiler warnings
329 ProcessingState processingState = PS_READ_HEADER;
330 ProcessingState returnState = PS_READ_HEADER;
331 bool hasMask = false;
332 quint64 payloadLength = 0;
336 switch (processingState) {
337 case PS_WAIT_FOR_MORE_DATA:
338 //TODO: waitForReadyRead should really be changed
339 //now, when a websocket is used in a GUI thread
340 //the GUI will hang for at most 5 seconds
341 //maybe, a QStateMachine should be used
342 if (!pIoDevice->waitForReadyRead(5000)) {
343 frame.setError(QWebSocketProtocol::CloseCodeGoingAway,
344 QObject::tr("Timeout when reading data from socket."));
345 processingState = PS_DISPATCH_RESULT;
347 processingState = returnState;
352 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
353 //FIN, RSV1-3, Opcode
354 char header[2] = {0};
355 bytesRead = pIoDevice->read(header, 2);
356 frame.m_isFinalFrame = (header[0] & 0x80) != 0;
357 frame.m_rsv1 = (header[0] & 0x40);
358 frame.m_rsv2 = (header[0] & 0x20);
359 frame.m_rsv3 = (header[0] & 0x10);
360 frame.m_opCode = static_cast<QWebSocketProtocol::OpCode>(header[0] & 0x0F);
362 //Mask, PayloadLength
363 hasMask = (header[1] & 0x80) != 0;
364 frame.m_length = (header[1] & 0x7F);
366 switch (frame.m_length)
370 processingState = PS_READ_PAYLOAD_LENGTH;
375 processingState = PS_READ_BIG_PAYLOAD_LENGTH;
380 payloadLength = frame.m_length;
381 processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
385 if (!frame.checkValidity())
386 processingState = PS_DISPATCH_RESULT;
388 WAIT_FOR_MORE_DATA(2);
392 case PS_READ_PAYLOAD_LENGTH:
393 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
394 uchar length[2] = {0};
395 bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 2);
396 if (Q_UNLIKELY(bytesRead == -1)) {
397 frame.setError(QWebSocketProtocol::CloseCodeGoingAway,
398 QObject::tr("Error occurred while reading from the network: %1")
399 .arg(pIoDevice->errorString()));
400 processingState = PS_DISPATCH_RESULT;
402 payloadLength = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(length));
403 if (Q_UNLIKELY(payloadLength < 126)) {
404 //see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
405 //"in all cases, the minimal number of bytes MUST be used to encode
406 //the length, for example, the length of a 124-byte-long string
407 //can't be encoded as the sequence 126, 0, 124"
408 frame.setError(QWebSocketProtocol::CloseCodeProtocolError,
409 QObject::tr("Lengths smaller than 126 " \
410 "must be expressed as one byte."));
411 processingState = PS_DISPATCH_RESULT;
413 processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
417 WAIT_FOR_MORE_DATA(2);
421 case PS_READ_BIG_PAYLOAD_LENGTH:
422 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 8)) {
423 uchar length[8] = {0};
424 bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 8);
425 if (Q_UNLIKELY(bytesRead < 8)) {
426 frame.setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
427 QObject::tr("Something went wrong during "\
428 "reading from the network."));
429 processingState = PS_DISPATCH_RESULT;
431 //Most significant bit must be set to 0 as
432 //per http://tools.ietf.org/html/rfc6455#section-5.2
433 payloadLength = qFromBigEndian<quint64>(length);
434 if (Q_UNLIKELY(payloadLength & (quint64(1) << 63))) {
435 frame.setError(QWebSocketProtocol::CloseCodeProtocolError,
436 QObject::tr("Highest bit of payload length is not 0."));
437 processingState = PS_DISPATCH_RESULT;
438 } else if (Q_UNLIKELY(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::CloseCodeProtocolError,
444 QObject::tr("Lengths smaller than 65536 (2^16) " \
445 "must be expressed as 2 bytes."));
446 processingState = PS_DISPATCH_RESULT;
448 processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
452 WAIT_FOR_MORE_DATA(8);
458 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 4)) {
459 bytesRead = pIoDevice->read(reinterpret_cast<char *>(&frame.m_mask),
460 sizeof(frame.m_mask));
461 if (bytesRead == -1) {
462 frame.setError(QWebSocketProtocol::CloseCodeGoingAway,
463 QObject::tr("Error while reading from the network: %1.")
464 .arg(pIoDevice->errorString()));
465 processingState = PS_DISPATCH_RESULT;
467 frame.m_mask = qFromBigEndian(frame.m_mask);
468 processingState = PS_READ_PAYLOAD;
471 WAIT_FOR_MORE_DATA(4);
475 case PS_READ_PAYLOAD:
476 if (!payloadLength) {
477 processingState = PS_DISPATCH_RESULT;
478 } else if (Q_UNLIKELY(payloadLength > MAX_FRAME_SIZE_IN_BYTES)) {
479 frame.setError(QWebSocketProtocol::CloseCodeTooMuchData,
480 QObject::tr("Maximum framesize exceeded."));
481 processingState = PS_DISPATCH_RESULT;
483 quint64 bytesAvailable = quint64(pIoDevice->bytesAvailable());
484 if (bytesAvailable >= payloadLength) {
485 frame.m_payload = pIoDevice->read(payloadLength);
486 //payloadLength can be safely cast to an integer,
487 //because MAX_FRAME_SIZE_IN_BYTES = MAX_INT
488 if (Q_UNLIKELY(frame.m_payload.length() != int(payloadLength))) {
489 //some error occurred; refer to the Qt documentation of QIODevice::read()
490 frame.setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
491 QObject::tr("Some serious error occurred " \
492 "while reading from the network."));
493 processingState = PS_DISPATCH_RESULT;
496 QWebSocketProtocol::mask(&frame.m_payload, frame.m_mask);
497 processingState = PS_DISPATCH_RESULT;
500 //if payload is too big, then this will timeout
501 WAIT_FOR_MORE_DATA(payloadLength);
506 case PS_DISPATCH_RESULT:
507 processingState = PS_READ_HEADER;
512 //should not come here
513 qWarning() << "DataProcessor::process: Found invalid state. This should not happen!";
526 void QWebSocketFrame::setError(QWebSocketProtocol::CloseCode code, const QString &closeReason)
530 m_closeReason = closeReason;
537 bool QWebSocketFrame::checkValidity()
541 if (Q_UNLIKELY(m_rsv1 || m_rsv2 || m_rsv3)) {
542 setError(QWebSocketProtocol::CloseCodeProtocolError, QObject::tr("Rsv field is non-zero"));
543 } else if (Q_UNLIKELY(QWebSocketProtocol::isOpCodeReserved(m_opCode))) {
544 setError(QWebSocketProtocol::CloseCodeProtocolError, QObject::tr("Used reserved opcode"));
545 } else if (isControlFrame()) {
546 if (Q_UNLIKELY(m_length > 125)) {
547 setError(QWebSocketProtocol::CloseCodeProtocolError,
548 QObject::tr("Controle frame is larger than 125 bytes"));
549 } else if (Q_UNLIKELY(!m_isFinalFrame)) {
550 setError(QWebSocketProtocol::CloseCodeProtocolError,
551 QObject::tr("Controle frames cannot be fragmented"));