summaryrefslogtreecommitdiff
path: root/src/websockets/qwebsocketdataprocessor.cpp
diff options
context:
space:
mode:
authorKurt Pattyn <pattyn.kurt@gmail.com>2014-01-12 16:09:08 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-01-13 17:18:27 +0100
commit334197b212551a3161df8379fe375870d12ab031 (patch)
tree0e2fedc8ac945bfb7b61eedb600561660fa7da32 /src/websockets/qwebsocketdataprocessor.cpp
parent9b0513ac67f0fa5d3658c48574f4f89c49c20945 (diff)
downloadqtwebsockets-334197b212551a3161df8379fe375870d12ab031.tar.gz
Remove _p suffix from cpp filenames.
Change-Id: I425aa372fe2f4114939f2691f270644e66017276 Reviewed-by: Kurt Pattyn <pattyn.kurt@gmail.com>
Diffstat (limited to 'src/websockets/qwebsocketdataprocessor.cpp')
-rw-r--r--src/websockets/qwebsocketdataprocessor.cpp302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/websockets/qwebsocketdataprocessor.cpp b/src/websockets/qwebsocketdataprocessor.cpp
new file mode 100644
index 0000000..620f8c4
--- /dev/null
+++ b/src/websockets/qwebsocketdataprocessor.cpp
@@ -0,0 +1,302 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtWebSockets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/*!
+ \class QWebSocketDataProcessor
+ The class QWebSocketDataProcessor is responsible for reading, validating and
+ interpreting data from a websocket.
+ It reads data from a QIODevice, validates it against RFC 6455, and parses it into
+ frames (data, control).
+ It emits signals that correspond to the type of the frame: textFrameReceived(),
+ binaryFrameReceived(), textMessageReceived(), binaryMessageReceived(), pingReceived(),
+ pongReceived() and closeReceived().
+ Whenever an error is detected, the errorEncountered() signal is emitted.
+ QWebSocketDataProcessor also checks if a frame is allowed in a sequence of frames
+ (e.g. a continuation frame cannot follow a final frame).
+ This class is an internal class used by QWebSocketInternal for data processing and validation.
+
+ \sa Frame()
+
+ \internal
+*/
+#include "qwebsocketdataprocessor_p.h"
+#include "qwebsocketprotocol.h"
+#include "qwebsocketprotocol_p.h"
+#include "qwebsocketframe_p.h"
+
+#include <QtCore/QtEndian>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextDecoder>
+#include <QtCore/QDebug>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ */
+QWebSocketDataProcessor::QWebSocketDataProcessor(QObject *parent) :
+ QObject(parent),
+ m_processingState(PS_READ_HEADER),
+ m_isFinalFrame(false),
+ m_isFragmented(false),
+ m_opCode(QWebSocketProtocol::OC_CLOSE),
+ m_isControlFrame(false),
+ m_hasMask(false),
+ m_mask(0),
+ m_binaryMessage(),
+ m_textMessage(),
+ m_payloadLength(0),
+ m_pConverterState(Q_NULLPTR),
+ m_pTextCodec(QTextCodec::codecForName("UTF-8"))
+{
+ clear();
+}
+
+/*!
+ \internal
+ */
+QWebSocketDataProcessor::~QWebSocketDataProcessor()
+{
+ clear();
+ if (m_pConverterState) {
+ delete m_pConverterState;
+ m_pConverterState = Q_NULLPTR;
+ }
+}
+
+/*!
+ \internal
+ */
+quint64 QWebSocketDataProcessor::maxMessageSize()
+{
+ return MAX_MESSAGE_SIZE_IN_BYTES; //COV_NF_LINE
+}
+
+/*!
+ \internal
+ */
+quint64 QWebSocketDataProcessor::maxFrameSize()
+{
+ return MAX_FRAME_SIZE_IN_BYTES;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
+{
+ bool isDone = false;
+
+ while (!isDone) {
+ QWebSocketFrame frame = QWebSocketFrame::readFrame(pIoDevice);
+ if (Q_LIKELY(frame.isValid())) {
+ if (frame.isControlFrame()) {
+ isDone = processControlFrame(frame);
+ } else {
+ //we have a dataframe; opcode can be OC_CONTINUE, OC_TEXT or OC_BINARY
+ if (Q_UNLIKELY(!m_isFragmented && frame.isContinuationFrame())) {
+ clear();
+ Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR,
+ tr("Received Continuation frame, while there is " \
+ "nothing to continue."));
+ return;
+ }
+ if (Q_UNLIKELY(m_isFragmented && frame.isDataFrame() &&
+ !frame.isContinuationFrame())) {
+ clear();
+ Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR,
+ tr("All data frames after the initial data frame " \
+ "must have opcode 0 (continuation)."));
+ return;
+ }
+ if (!frame.isContinuationFrame()) {
+ m_opCode = frame.opCode();
+ m_isFragmented = !frame.isFinalFrame();
+ }
+ quint64 messageLength = (quint64)(m_opCode == QWebSocketProtocol::OC_TEXT)
+ ? m_textMessage.length()
+ : m_binaryMessage.length();
+ if (Q_UNLIKELY((messageLength + quint64(frame.payload().length())) >
+ MAX_MESSAGE_SIZE_IN_BYTES)) {
+ clear();
+ Q_EMIT errorEncountered(QWebSocketProtocol::CC_TOO_MUCH_DATA,
+ tr("Received message is too big."));
+ return;
+ }
+
+ if (m_opCode == QWebSocketProtocol::OC_TEXT) {
+ QString frameTxt = m_pTextCodec->toUnicode(frame.payload().constData(),
+ frame.payload().size(),
+ m_pConverterState);
+ bool failed = (m_pConverterState->invalidChars != 0)
+ || (frame.isFinalFrame() && (m_pConverterState->remainingChars != 0));
+ if (Q_UNLIKELY(failed)) {
+ clear();
+ Q_EMIT errorEncountered(QWebSocketProtocol::CC_WRONG_DATATYPE,
+ tr("Invalid UTF-8 code encountered."));
+ return;
+ } else {
+ m_textMessage.append(frameTxt);
+ Q_EMIT textFrameReceived(frameTxt, frame.isFinalFrame());
+ }
+ } else {
+ m_binaryMessage.append(frame.payload());
+ Q_EMIT binaryFrameReceived(frame.payload(), frame.isFinalFrame());
+ }
+
+ if (frame.isFinalFrame()) {
+ if (m_opCode == QWebSocketProtocol::OC_TEXT)
+ Q_EMIT textMessageReceived(m_textMessage);
+ else
+ Q_EMIT binaryMessageReceived(m_binaryMessage);
+ clear();
+ isDone = true;
+ }
+ }
+ } else {
+ Q_EMIT errorEncountered(frame.closeCode(), frame.closeReason());
+ clear();
+ isDone = true;
+ }
+ }
+}
+
+/*!
+ \internal
+ */
+void QWebSocketDataProcessor::clear()
+{
+ m_processingState = PS_READ_HEADER;
+ m_isFinalFrame = false;
+ m_isFragmented = false;
+ m_opCode = QWebSocketProtocol::OC_CLOSE;
+ m_hasMask = false;
+ m_mask = 0;
+ m_binaryMessage.clear();
+ m_textMessage.clear();
+ m_payloadLength = 0;
+ if (m_pConverterState) {
+ if ((m_pConverterState->remainingChars != 0) || (m_pConverterState->invalidChars != 0)) {
+ delete m_pConverterState;
+ m_pConverterState = Q_NULLPTR;
+ }
+ }
+ if (!m_pConverterState)
+ m_pConverterState = new QTextCodec::ConverterState(QTextCodec::ConvertInvalidToNull |
+ QTextCodec::IgnoreHeader);
+}
+
+/*!
+ \internal
+ */
+bool QWebSocketDataProcessor::processControlFrame(const QWebSocketFrame &frame)
+{
+ bool mustStopProcessing = true; //control frames never expect additional frames to be processed
+ switch (frame.opCode()) {
+ case QWebSocketProtocol::OC_PING:
+ Q_EMIT pingReceived(frame.payload());
+ break;
+
+ case QWebSocketProtocol::OC_PONG:
+ Q_EMIT pongReceived(frame.payload());
+ break;
+
+ case QWebSocketProtocol::OC_CLOSE:
+ {
+ quint16 closeCode = QWebSocketProtocol::CC_NORMAL;
+ QString closeReason;
+ QByteArray payload = frame.payload();
+ if (Q_UNLIKELY(payload.size() == 1)) {
+ //size is either 0 (no close code and no reason)
+ //or >= 2 (at least a close code of 2 bytes)
+ closeCode = QWebSocketProtocol::CC_PROTOCOL_ERROR;
+ closeReason = tr("Payload of close frame is too small.");
+ } else if (Q_LIKELY(payload.size() > 1)) {
+ //close frame can have a close code and reason
+ closeCode = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(payload.constData()));
+ if (Q_UNLIKELY(!QWebSocketProtocol::isCloseCodeValid(closeCode))) {
+ closeCode = QWebSocketProtocol::CC_PROTOCOL_ERROR;
+ closeReason = tr("Invalid close code %1 detected.").arg(closeCode);
+ } else {
+ if (payload.size() > 2) {
+ QTextCodec *tc = QTextCodec::codecForName(QByteArrayLiteral("UTF-8"));
+ QTextCodec::ConverterState state(QTextCodec::ConvertInvalidToNull);
+ closeReason = tc->toUnicode(payload.constData() + 2, payload.size() - 2, &state);
+ const bool failed = (state.invalidChars != 0) || (state.remainingChars != 0);
+ if (Q_UNLIKELY(failed)) {
+ closeCode = QWebSocketProtocol::CC_WRONG_DATATYPE;
+ closeReason = tr("Invalid UTF-8 code encountered.");
+ }
+ }
+ }
+ }
+ Q_EMIT closeReceived(static_cast<QWebSocketProtocol::CloseCode>(closeCode), closeReason);
+ break;
+ }
+
+ case QWebSocketProtocol::OC_CONTINUE:
+ case QWebSocketProtocol::OC_BINARY:
+ case QWebSocketProtocol::OC_TEXT:
+ case QWebSocketProtocol::OC_RESERVED_3:
+ case QWebSocketProtocol::OC_RESERVED_4:
+ case QWebSocketProtocol::OC_RESERVED_5:
+ case QWebSocketProtocol::OC_RESERVED_6:
+ case QWebSocketProtocol::OC_RESERVED_7:
+ case QWebSocketProtocol::OC_RESERVED_C:
+ case QWebSocketProtocol::OC_RESERVED_B:
+ case QWebSocketProtocol::OC_RESERVED_D:
+ case QWebSocketProtocol::OC_RESERVED_E:
+ case QWebSocketProtocol::OC_RESERVED_F:
+ //do nothing
+ //case statements added to make C++ compiler happy
+ break;
+
+ default:
+ Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR,
+ tr("Invalid opcode detected: %1").arg(int(frame.opCode())));
+ //do nothing
+ break;
+ }
+ return mustStopProcessing;
+}
+
+QT_END_NAMESPACE