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:LGPL21$
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 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
26 ** In addition, as a special exception, Digia gives you certain additional
27 ** rights. These rights are described in the Digia Qt LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
32 ****************************************************************************/
35 \class QWebSocketFrame
36 The class QWebSocketFrame is responsible for reading, validating and
37 interpreting frames from a WebSocket.
38 It reads data from a QIODevice, validates it against RFC 6455, and parses it into a
39 frame (data, control).
40 Whenever an error is detected, isValid() returns false.
42 \note The QWebSocketFrame class does not look at valid sequences of frames.
43 It processes frames one at a time.
44 \note It is the QWebSocketDataProcessor that takes the sequence into account.
46 \sa QWebSocketDataProcessor
50 #include "qwebsocketframe_p.h"
51 #include "qwebsocketprotocol_p.h"
53 #include <QtCore/QtEndian>
54 #include <QtCore/QDebug>
61 QWebSocketFrame::QWebSocketFrame() :
62 m_closeCode(QWebSocketProtocol::CloseCodeNormal),
69 m_opCode(QWebSocketProtocol::OpCodeReservedC),
79 QWebSocketFrame::QWebSocketFrame(const QWebSocketFrame &other) :
80 m_closeCode(other.m_closeCode),
81 m_closeReason(other.m_closeReason),
82 m_isFinalFrame(other.m_isFinalFrame),
87 m_opCode(other.m_opCode),
88 m_length(other.m_length),
89 m_payload(other.m_payload),
90 m_isValid(other.m_isValid)
97 QWebSocketFrame &QWebSocketFrame::operator =(const QWebSocketFrame &other)
99 m_closeCode = other.m_closeCode;
100 m_closeReason = other.m_closeReason;
101 m_isFinalFrame = other.m_isFinalFrame;
102 m_mask = other.m_mask;
103 m_rsv1 = other.m_rsv1;
104 m_rsv2 = other.m_rsv2;
105 m_rsv3 = other.m_rsv2;
106 m_opCode = other.m_opCode;
107 m_length = other.m_length;
108 m_payload = other.m_payload;
109 m_isValid = other.m_isValid;
114 #ifdef Q_COMPILER_RVALUE_REFS
118 QWebSocketFrame::QWebSocketFrame(QWebSocketFrame &&other) :
119 m_closeCode(qMove(other.m_closeCode)),
120 m_closeReason(qMove(other.m_closeReason)),
121 m_isFinalFrame(qMove(other.m_isFinalFrame)),
122 m_mask(qMove(other.m_mask)),
123 m_rsv1(qMove(other.m_rsv1)),
124 m_rsv2(qMove(other.m_rsv2)),
125 m_rsv3(qMove(other.m_rsv3)),
126 m_opCode(qMove(other.m_opCode)),
127 m_length(qMove(other.m_length)),
128 m_payload(qMove(other.m_payload)),
129 m_isValid(qMove(other.m_isValid))
136 QWebSocketFrame &QWebSocketFrame::operator =(QWebSocketFrame &&other)
138 qSwap(m_closeCode, other.m_closeCode);
139 qSwap(m_closeReason, other.m_closeReason);
140 qSwap(m_isFinalFrame, other.m_isFinalFrame);
141 qSwap(m_mask, other.m_mask);
142 qSwap(m_rsv1, other.m_rsv1);
143 qSwap(m_rsv2, other.m_rsv2);
144 qSwap(m_rsv3, other.m_rsv3);
145 qSwap(m_opCode, other.m_opCode);
146 qSwap(m_length, other.m_length);
147 qSwap(m_payload, other.m_payload);
148 qSwap(m_isValid, other.m_isValid);
158 void QWebSocketFrame::swap(QWebSocketFrame &other)
160 if (&other != this) {
161 qSwap(m_closeCode, other.m_closeCode);
162 qSwap(m_closeReason, other.m_closeReason);
163 qSwap(m_isFinalFrame, other.m_isFinalFrame);
164 qSwap(m_mask, other.m_mask);
165 qSwap(m_rsv1, other.m_rsv1);
166 qSwap(m_rsv2, other.m_rsv2);
167 qSwap(m_rsv3, other.m_rsv3);
168 qSwap(m_opCode, other.m_opCode);
169 qSwap(m_length, other.m_length);
170 qSwap(m_payload, other.m_payload);
171 qSwap(m_isValid, other.m_isValid);
178 QWebSocketProtocol::CloseCode QWebSocketFrame::closeCode() const
186 QString QWebSocketFrame::closeReason() const
188 return m_closeReason;
194 bool QWebSocketFrame::isFinalFrame() const
196 return m_isFinalFrame;
202 bool QWebSocketFrame::isControlFrame() const
204 return (m_opCode & 0x08) == 0x08;
210 bool QWebSocketFrame::isDataFrame() const
212 return !isControlFrame();
218 bool QWebSocketFrame::isContinuationFrame() const
220 return isDataFrame() && (m_opCode == QWebSocketProtocol::OpCodeContinue);
226 bool QWebSocketFrame::hasMask() const
234 quint32 QWebSocketFrame::mask() const
242 int QWebSocketFrame::rsv1() const
250 int QWebSocketFrame::rsv2() const
258 int QWebSocketFrame::rsv3() const
266 QWebSocketProtocol::OpCode QWebSocketFrame::opCode() const
274 QByteArray QWebSocketFrame::payload() const
280 Resets all member variables, and invalidates the object.
284 void QWebSocketFrame::clear()
286 m_closeCode = QWebSocketProtocol::CloseCodeNormal;
287 m_closeReason.clear();
288 m_isFinalFrame = true;
293 m_opCode = QWebSocketProtocol::OpCodeReservedC;
302 bool QWebSocketFrame::isValid() const
307 // The arm compiler of Visual Studio 2013 Update 3 crashes when
308 // trying to optimize QWebSocketFrame::readFrame. Hence turn
309 // those off for this snippet
310 #if defined(Q_OS_WINPHONE) && defined(__ARM__)
311 # pragma optimize("", off)
314 #define WAIT_FOR_MORE_DATA(dataSizeInBytes) \
315 { returnState = processingState; \
316 processingState = PS_WAIT_FOR_MORE_DATA; dataWaitSize = dataSizeInBytes; }
321 QWebSocketFrame QWebSocketFrame::readFrame(QIODevice *pIoDevice)
324 qint64 bytesRead = 0;
325 QWebSocketFrame frame;
326 quint64 dataWaitSize = 0;
327 Q_UNUSED(dataWaitSize); // value is used in MACRO, Q_UNUSED to avoid compiler warnings
328 ProcessingState processingState = PS_READ_HEADER;
329 ProcessingState returnState = PS_READ_HEADER;
330 bool hasMask = false;
331 quint64 payloadLength = 0;
335 switch (processingState) {
336 case PS_WAIT_FOR_MORE_DATA:
337 //TODO: waitForReadyRead should really be changed
338 //now, when a WebSocket is used in a GUI thread
339 //the GUI will hang for at most 5 seconds
340 //maybe, a QStateMachine should be used
341 if (!pIoDevice->waitForReadyRead(5000)) {
342 frame.setError(QWebSocketProtocol::CloseCodeGoingAway,
343 QObject::tr("Timeout when reading data from socket."));
344 processingState = PS_DISPATCH_RESULT;
346 processingState = returnState;
351 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
352 //FIN, RSV1-3, Opcode
353 char header[2] = {0};
354 bytesRead = pIoDevice->read(header, 2);
355 frame.m_isFinalFrame = (header[0] & 0x80) != 0;
356 frame.m_rsv1 = (header[0] & 0x40);
357 frame.m_rsv2 = (header[0] & 0x20);
358 frame.m_rsv3 = (header[0] & 0x10);
359 frame.m_opCode = static_cast<QWebSocketProtocol::OpCode>(header[0] & 0x0F);
361 //Mask, PayloadLength
362 hasMask = (header[1] & 0x80) != 0;
363 frame.m_length = (header[1] & 0x7F);
365 switch (frame.m_length)
369 processingState = PS_READ_PAYLOAD_LENGTH;
374 processingState = PS_READ_BIG_PAYLOAD_LENGTH;
379 payloadLength = frame.m_length;
380 processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
384 if (!frame.checkValidity())
385 processingState = PS_DISPATCH_RESULT;
387 WAIT_FOR_MORE_DATA(2);
391 case PS_READ_PAYLOAD_LENGTH:
392 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
393 uchar length[2] = {0};
394 bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 2);
395 if (Q_UNLIKELY(bytesRead == -1)) {
396 frame.setError(QWebSocketProtocol::CloseCodeGoingAway,
397 QObject::tr("Error occurred while reading from the network: %1")
398 .arg(pIoDevice->errorString()));
399 processingState = PS_DISPATCH_RESULT;
401 payloadLength = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(length));
402 if (Q_UNLIKELY(payloadLength < 126)) {
403 //see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
404 //"in all cases, the minimal number of bytes MUST be used to encode
405 //the length, for example, the length of a 124-byte-long string
406 //can't be encoded as the sequence 126, 0, 124"
407 frame.setError(QWebSocketProtocol::CloseCodeProtocolError,
408 QObject::tr("Lengths smaller than 126 " \
409 "must be expressed as one byte."));
410 processingState = PS_DISPATCH_RESULT;
412 processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
416 WAIT_FOR_MORE_DATA(2);
420 case PS_READ_BIG_PAYLOAD_LENGTH:
421 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 8)) {
422 uchar length[8] = {0};
423 bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 8);
424 if (Q_UNLIKELY(bytesRead < 8)) {
425 frame.setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
426 QObject::tr("Something went wrong during "\
427 "reading from the network."));
428 processingState = PS_DISPATCH_RESULT;
430 //Most significant bit must be set to 0 as
431 //per http://tools.ietf.org/html/rfc6455#section-5.2
432 payloadLength = qFromBigEndian<quint64>(length);
433 if (Q_UNLIKELY(payloadLength & (quint64(1) << 63))) {
434 frame.setError(QWebSocketProtocol::CloseCodeProtocolError,
435 QObject::tr("Highest bit of payload length is not 0."));
436 processingState = PS_DISPATCH_RESULT;
437 } else if (Q_UNLIKELY(payloadLength <= 0xFFFFu)) {
438 //see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
439 //"in all cases, the minimal number of bytes MUST be used to encode
440 //the length, for example, the length of a 124-byte-long string
441 //can't be encoded as the sequence 126, 0, 124"
442 frame.setError(QWebSocketProtocol::CloseCodeProtocolError,
443 QObject::tr("Lengths smaller than 65536 (2^16) " \
444 "must be expressed as 2 bytes."));
445 processingState = PS_DISPATCH_RESULT;
447 processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
451 WAIT_FOR_MORE_DATA(8);
457 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 4)) {
458 bytesRead = pIoDevice->read(reinterpret_cast<char *>(&frame.m_mask),
459 sizeof(frame.m_mask));
460 if (bytesRead == -1) {
461 frame.setError(QWebSocketProtocol::CloseCodeGoingAway,
462 QObject::tr("Error while reading from the network: %1.")
463 .arg(pIoDevice->errorString()));
464 processingState = PS_DISPATCH_RESULT;
466 frame.m_mask = qFromBigEndian(frame.m_mask);
467 processingState = PS_READ_PAYLOAD;
470 WAIT_FOR_MORE_DATA(4);
474 case PS_READ_PAYLOAD:
475 if (!payloadLength) {
476 processingState = PS_DISPATCH_RESULT;
477 } else if (Q_UNLIKELY(payloadLength > MAX_FRAME_SIZE_IN_BYTES)) {
478 frame.setError(QWebSocketProtocol::CloseCodeTooMuchData,
479 QObject::tr("Maximum framesize exceeded."));
480 processingState = PS_DISPATCH_RESULT;
482 quint64 bytesAvailable = quint64(pIoDevice->bytesAvailable());
483 if (bytesAvailable >= payloadLength) {
484 frame.m_payload = pIoDevice->read(payloadLength);
485 //payloadLength can be safely cast to an integer,
486 //because MAX_FRAME_SIZE_IN_BYTES = MAX_INT
487 if (Q_UNLIKELY(frame.m_payload.length() != int(payloadLength))) {
488 //some error occurred; refer to the Qt documentation of QIODevice::read()
489 frame.setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
490 QObject::tr("Some serious error occurred " \
491 "while reading from the network."));
492 processingState = PS_DISPATCH_RESULT;
495 QWebSocketProtocol::mask(&frame.m_payload, frame.m_mask);
496 processingState = PS_DISPATCH_RESULT;
499 //if payload is too big, then this will timeout
500 WAIT_FOR_MORE_DATA(payloadLength);
505 case PS_DISPATCH_RESULT:
506 processingState = PS_READ_HEADER;
511 //should not come here
512 qWarning() << "DataProcessor::process: Found invalid state. This should not happen!";
522 #if defined(Q_OS_WINPHONE) && defined(__ARM__)
523 # pragma optimize("", on)
529 void QWebSocketFrame::setError(QWebSocketProtocol::CloseCode code, const QString &closeReason)
533 m_closeReason = closeReason;
540 bool QWebSocketFrame::checkValidity()
542 if (Q_UNLIKELY(m_rsv1 || m_rsv2 || m_rsv3)) {
543 setError(QWebSocketProtocol::CloseCodeProtocolError, QObject::tr("Rsv field is non-zero"));
544 } else if (Q_UNLIKELY(QWebSocketProtocol::isOpCodeReserved(m_opCode))) {
545 setError(QWebSocketProtocol::CloseCodeProtocolError, QObject::tr("Used reserved opcode"));
546 } else if (isControlFrame()) {
547 if (Q_UNLIKELY(m_length > 125)) {
548 setError(QWebSocketProtocol::CloseCodeProtocolError,
549 QObject::tr("Controle frame is larger than 125 bytes"));
550 } else if (Q_UNLIKELY(!m_isFinalFrame)) {
551 setError(QWebSocketProtocol::CloseCodeProtocolError,
552 QObject::tr("Controle frames cannot be fragmented"));