summaryrefslogtreecommitdiff
path: root/tests/auto/websockets
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@theqtcompany.com>2015-05-20 14:34:48 +0200
committerLiang Qi <liang.qi@theqtcompany.com>2015-05-22 07:13:34 +0000
commitfaab6ff98dafa564f2c58bd95689ffba6d3a4e32 (patch)
treeeef653458a66dd1c0e903d202540718a921147e9 /tests/auto/websockets
parent0a9cbcf2894f4f9e621e0f5fb644d2b5e15ab2c7 (diff)
downloadqtwebsockets-faab6ff98dafa564f2c58bd95689ffba6d3a4e32.tar.gz
tests: move all auto tests to websockets sub directory
cmake is not included. Change-Id: I9ab6244d8b6b3752f26070b0b15ae70c0cab6d82 Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'tests/auto/websockets')
-rw-r--r--tests/auto/websockets/dataprocessor/dataprocessor.pro14
-rw-r--r--tests/auto/websockets/dataprocessor/tst_dataprocessor.cpp1842
-rw-r--r--tests/auto/websockets/handshakerequest/handshakerequest.pro14
-rw-r--r--tests/auto/websockets/handshakerequest/tst_handshakerequest.cpp317
-rw-r--r--tests/auto/websockets/handshakeresponse/handshakeresponse.pro14
-rw-r--r--tests/auto/websockets/handshakeresponse/tst_handshakeresponse.cpp112
-rw-r--r--tests/auto/websockets/qdefaultmaskgenerator/qdefaultmaskgenerator.pro14
-rw-r--r--tests/auto/websockets/qdefaultmaskgenerator/tst_defaultmaskgenerator.cpp150
-rw-r--r--tests/auto/websockets/qwebsocket/qwebsocket.pro13
-rw-r--r--tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp605
-rw-r--r--tests/auto/websockets/qwebsocketcorsauthenticator/qwebsocketcorsauthenticator.pro13
-rw-r--r--tests/auto/websockets/qwebsocketcorsauthenticator/tst_qwebsocketcorsauthenticator.cpp112
-rw-r--r--tests/auto/websockets/qwebsocketserver/qwebsocketserver.pro13
-rw-r--r--tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp390
-rw-r--r--tests/auto/websockets/websocketframe/tst_websocketframe.cpp624
-rw-r--r--tests/auto/websockets/websocketframe/websocketframe.pro14
-rw-r--r--tests/auto/websockets/websocketprotocol/tst_websocketprotocol.cpp289
-rw-r--r--tests/auto/websockets/websocketprotocol/websocketprotocol.pro14
-rw-r--r--tests/auto/websockets/websockets.pro16
19 files changed, 4580 insertions, 0 deletions
diff --git a/tests/auto/websockets/dataprocessor/dataprocessor.pro b/tests/auto/websockets/dataprocessor/dataprocessor.pro
new file mode 100644
index 0000000..ac5ba5c
--- /dev/null
+++ b/tests/auto/websockets/dataprocessor/dataprocessor.pro
@@ -0,0 +1,14 @@
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+TARGET = tst_dataprocessor
+
+QT = core testlib websockets websockets-private
+
+SOURCES += tst_dataprocessor.cpp
+
+requires(contains(QT_CONFIG, private_tests))
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/websockets/dataprocessor/tst_dataprocessor.cpp b/tests/auto/websockets/dataprocessor/tst_dataprocessor.cpp
new file mode 100644
index 0000000..660d8eb
--- /dev/null
+++ b/tests/auto/websockets/dataprocessor/tst_dataprocessor.cpp
@@ -0,0 +1,1842 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QtTest/qtestcase.h>
+#include <QtTest/QSignalSpy>
+#include <QtCore/QBuffer>
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+
+#include "private/qwebsocketdataprocessor_p.h"
+#include "private/qwebsocketprotocol_p.h"
+#include "QtWebSockets/qwebsocketprotocol.h"
+
+const quint8 FIN = 0x80;
+const quint8 RSV1 = 0x40;
+const quint8 RSV2 = 0x30;
+const quint8 RSV3 = 0x10;
+
+QT_USE_NAMESPACE
+
+Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
+Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
+
+class tst_DataProcessor : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_DataProcessor();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ /***************************************************************************
+ * Happy Flows
+ ***************************************************************************/
+ /*!
+ Tests all kinds of valid binary frames, including zero length frames
+ */
+ void goodBinaryFrame();
+ void goodBinaryFrame_data();
+
+ /*!
+ Tests all kinds of valid text frames, including zero length frames
+ */
+ void goodTextFrame();
+ void goodTextFrame_data();
+
+ /*!
+ * Test all kinds of valid control frames.
+ */
+ void goodControlFrame();
+
+ /*!
+ * Test all kinds of valid close frames.
+ */
+ void goodCloseFrame();
+ void goodCloseFrame_data();
+
+ /*!
+ * Test all valid opcodes
+ */
+ void goodOpcodes();
+ void goodOpcodes_data();
+
+ /*!
+ Tests the QWebSocketDataProcessor for the correct handling of non-charactercodes
+ Due to a workaround in QTextCodec, non-characters are treated as illegal.
+ This workaround is not necessary anymore, and hence code should be changed in Qt
+ to allow non-characters again.
+ */
+ void nonCharacterCodes();
+ void nonCharacterCodes_data();
+
+ /***************************************************************************
+ * Rainy Day Flows
+ ***************************************************************************/
+ /*!
+ Tests the QWebSocketDataProcessor for correct handling of frames that don't
+ contain the starting 2 bytes.
+ This test is a border case, where not enough bytes are received to even start parsing a
+ frame.
+ This test does not test sequences of frames, only single frames are tested
+ */
+ void frameTooSmall();
+
+ /*!
+ Tests the QWebSocketDataProcessor for correct handling of frames that are oversized.
+ This test does not test sequences of frames, only single frames are tested
+ */
+ void frameTooBig();
+ void frameTooBig_data();
+
+ /*!
+ Tests the QWebSocketDataProcessor for the correct handling of malformed frame headers.
+ This test does not test sequences of frames, only single frames are tested
+ */
+ void invalidHeader();
+ void invalidHeader_data();
+
+ /*!
+ Tests the QWebSocketDataProcessor for the correct handling of invalid control frames.
+ Invalid means: payload bigger than 125, frame is fragmented, ...
+ This test does not test sequences of frames, only single frames are tested
+ */
+ void invalidControlFrame();
+ void invalidControlFrame_data();
+
+ void invalidCloseFrame();
+ void invalidCloseFrame_data();
+
+ /*!
+ Tests the QWebSocketDataProcessor for the correct handling of incomplete size fields
+ for large and big payloads.
+ */
+ void incompleteSizeField();
+ void incompleteSizeField_data();
+
+ /*!
+ Tests the QWebSocketDataProcessor for the correct handling of incomplete payloads.
+ This includes:
+ - incomplete length bytes for large and big payloads (16- and 64-bit values),
+ - minimum size representation (see RFC 6455 paragraph 5.2),
+ - frames that are too large (larger than MAX_INT in bytes)
+ - incomplete payloads (less bytes than indicated in the size field)
+ This test does not test sequences of frames, only single frames are tested
+ */
+ void incompletePayload();
+ void incompletePayload_data();
+
+ /*!
+ Tests the QWebSocketDataProcessor for the correct handling of invalid UTF-8 payloads.
+ This test does not test sequences of frames, only single frames are tested
+ */
+ void invalidPayload();
+ void invalidPayload_data(bool isControlFrame = false);
+
+ void invalidPayloadInCloseFrame();
+ void invalidPayloadInCloseFrame_data();
+
+ /*!
+ Tests the QWebSocketDataProcessor for the correct handling of the minimum size representation
+ requirement of RFC 6455 (see paragraph 5.2)
+ */
+ void minimumSizeRequirement();
+ void minimumSizeRequirement_data();
+
+private:
+ //helper function that constructs a new row of test data for invalid UTF8 sequences
+ void invalidUTF8(const char *dataTag, const char *utf8Sequence, bool isCloseFrame);
+ //helper function that constructs a new row of test data for invalid leading field values
+ void invalidField(const char *dataTag, quint8 invalidFieldValue);
+ //helper functions that construct a new row of test data for size fields that do not adhere
+ //to the minimum size requirement
+ void minimumSize16Bit(quint16 sizeInBytes);
+ void minimumSize64Bit(quint64 sizeInBytes);
+ //helper function to construct a new row of test data containing frames with a payload size
+ //smaller than indicated in the header
+ void incompleteFrame(quint8 controlCode, quint64 indicatedSize, quint64 actualPayloadSize);
+ void insertIncompleteSizeFieldTest(quint8 payloadCode, quint8 numBytesFollowing);
+
+ //helper function to construct a new row of test data containing text frames containing
+ //sequences
+ void nonCharacterSequence(const char *sequence);
+
+ void doTest();
+ void doCloseFrameTest();
+
+ QString opCodeToString(quint8 opCode);
+};
+
+tst_DataProcessor::tst_DataProcessor()
+{
+}
+
+void tst_DataProcessor::initTestCase()
+{
+}
+
+void tst_DataProcessor::cleanupTestCase()
+{
+}
+
+void tst_DataProcessor::init()
+{
+ qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode");
+ qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
+}
+
+void tst_DataProcessor::cleanup()
+{
+}
+
+void tst_DataProcessor::goodBinaryFrame_data()
+{
+ QTest::addColumn<QByteArray>("payload");
+ //be sure to get small (< 126 bytes), large (> 125 bytes & < 64K) and big (>64K) frames
+ for (int i = 0; i < (65536 + 256); i += 128)
+ {
+ QTest::newRow(QStringLiteral("Binary frame with %1 bytes").arg(i).toLatin1().constData())
+ << QByteArray(i, char(1));
+ }
+ for (int i = 0; i < 256; ++i) //test all possible bytes in the payload
+ {
+ QTest::newRow(QStringLiteral("Binary frame containing byte: '0x%1'")
+ .arg(QByteArray(1, char(i)).toHex().constData()).toLatin1().constData())
+ << QByteArray(i, char(1));
+ }
+}
+
+void tst_DataProcessor::goodBinaryFrame()
+{
+ QByteArray data;
+ QBuffer buffer;
+ QWebSocketDataProcessor dataProcessor;
+ QFETCH(QByteArray, payload);
+
+ data.append((char)(FIN | QWebSocketProtocol::OpCodeBinary));
+
+ if (payload.length() < 126)
+ {
+ data.append(char(payload.length()));
+ }
+ else if (payload.length() < 65536)
+ {
+ quint16 swapped = qToBigEndian<quint16>(payload.length());
+ const char *wireRepresentation
+ = static_cast<const char *>(static_cast<const void *>(&swapped));
+ data.append(char(126)).append(wireRepresentation, 2);
+ }
+ else
+ {
+ quint64 swapped = qToBigEndian<quint64>(payload.length());
+ const char *wireRepresentation
+ = static_cast<const char *>(static_cast<const void *>(&swapped));
+ data.append(char(127)).append(wireRepresentation, 8);
+ }
+
+ data.append(payload);
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+
+ QSignalSpy errorReceivedSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy closeReceivedSpy(&dataProcessor,
+ SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy pingReceivedSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray)));
+ QSignalSpy pongReceivedSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray)));
+ QSignalSpy binaryFrameReceivedSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+ QSignalSpy binaryMessageReceivedSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
+ QSignalSpy textFrameReceivedSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy textMessageReceivedSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
+ dataProcessor.process(&buffer);
+ QCOMPARE(errorReceivedSpy.count(), 0);
+ QCOMPARE(pingReceivedSpy.count(), 0);
+ QCOMPARE(pongReceivedSpy.count(), 0);
+ QCOMPARE(closeReceivedSpy.count(), 0);
+ QCOMPARE(binaryFrameReceivedSpy.count(), 1);
+ QCOMPARE(binaryMessageReceivedSpy.count(), 1);
+ QCOMPARE(textFrameReceivedSpy.count(), 0);
+ QCOMPARE(textMessageReceivedSpy.count(), 0);
+ QList<QVariant> arguments = binaryFrameReceivedSpy.takeFirst();
+ QCOMPARE(arguments.at(0).toByteArray().length(), payload.length());
+ arguments = binaryMessageReceivedSpy.takeFirst();
+ QCOMPARE(arguments.at(0).toByteArray().length(), payload.length());
+ buffer.close();
+}
+
+void tst_DataProcessor::goodTextFrame_data()
+{
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<int>("size");
+
+ //test frames with small (< 126), large ( < 65536) and big ( > 65535) payloads
+ for (int i = 0; i < (65536 + 256); i += 128)
+ {
+ QTest::newRow(QStringLiteral("Text frame with %1 ASCII characters")
+ .arg(i).toLatin1().constData()) << QByteArray(i, 'a') << i;
+ }
+ //test all valid ASCII characters
+ for (int i = 0; i < 128; ++i)
+ {
+ QTest::newRow(QStringLiteral("Text frame with containing ASCII character '0x%1'")
+ .arg(QByteArray(1, char(i)).toHex().constData()).toLatin1().constData())
+ << QByteArray(1, char(i)) << 1;
+ }
+
+ //the text string reads: Text frame containing Hello-µ@ßöäüàá-UTF-8!!
+ //Visual Studio doesn't like UTF-8 in source code, so we use escape codes for the string
+ //The number 22 refers to the length of the string;
+ //the length was incorrectly calculated on Visual Studio
+
+ //doing extensive QStringLiteral concatenations here, because
+ //MSVC 2010 complains when using concatenation literal strings about
+ //concatenation of wide and narrow strings:
+ //error C2308: concatenating mismatched strings
+ QTest::newRow((QStringLiteral("Text frame containing Hello-") +
+ QStringLiteral("\xC2\xB5\x40\xC3\x9F\xC3\xB6\xC3\xA4\xC3\xBC\xC3\xA0") +
+ QStringLiteral("\xC3\xA1-UTF-8!!")).toLatin1().constData())
+ << QByteArray::fromHex("48656c6c6f2dc2b540c39fc3b6c3a4c3bcc3a0c3a12d5554462d382121")
+ << 22;
+}
+
+void tst_DataProcessor::goodTextFrame()
+{
+ QByteArray data;
+ QBuffer buffer;
+ QWebSocketDataProcessor dataProcessor;
+ QFETCH(QByteArray, payload);
+ QFETCH(int, size);
+
+ data.append((char)(FIN | QWebSocketProtocol::OpCodeText));
+
+ if (payload.length() < 126)
+ {
+ data.append(char(payload.length()));
+ }
+ else if (payload.length() < 65536)
+ {
+ quint16 swapped = qToBigEndian<quint16>(payload.length());
+ const char *wireRepresentation
+ = static_cast<const char *>(static_cast<const void *>(&swapped));
+ data.append(char(126)).append(wireRepresentation, 2);
+ }
+ else
+ {
+ quint64 swapped = qToBigEndian<quint64>(payload.length());
+ const char *wireRepresentation
+ = static_cast<const char *>(static_cast<const void *>(&swapped));
+ data.append(char(127)).append(wireRepresentation, 8);
+ }
+
+ data.append(payload);
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+
+ QSignalSpy errorReceivedSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy closeReceivedSpy(&dataProcessor,
+ SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy pingReceivedSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray)));
+ QSignalSpy pongReceivedSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray)));
+ QSignalSpy textFrameReceivedSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy textMessageReceivedSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryFrameReceivedSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+ QSignalSpy binaryMessageReceivedSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
+
+ dataProcessor.process(&buffer);
+
+ QCOMPARE(errorReceivedSpy.count(), 0);
+ QCOMPARE(pingReceivedSpy.count(), 0);
+ QCOMPARE(pongReceivedSpy.count(), 0);
+ QCOMPARE(closeReceivedSpy.count(), 0);
+ QCOMPARE(textFrameReceivedSpy.count(), 1);
+ QCOMPARE(textMessageReceivedSpy.count(), 1);
+ QCOMPARE(binaryFrameReceivedSpy.count(), 0);
+ QCOMPARE(binaryMessageReceivedSpy.count(), 0);
+ QList<QVariant> arguments = textFrameReceivedSpy.takeFirst();
+ QCOMPARE(arguments.at(0).toString().length(), size);
+ arguments = textMessageReceivedSpy.takeFirst();
+ QCOMPARE(arguments.at(0).toString().length(), size);
+ buffer.close();
+}
+
+void tst_DataProcessor::goodControlFrame()
+{
+ QByteArray data;
+ QBuffer buffer;
+ QWebSocketDataProcessor dataProcessor;
+
+ QSignalSpy closeFrameReceivedSpy(&dataProcessor,
+ SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy errorReceivedSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy textFrameReceivedSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy textMessageReceivedSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryFrameReceivedSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+ QSignalSpy binaryMessageReceivedSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
+ QSignalSpy pingReceivedSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray)));
+ QSignalSpy pongReceivedSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray)));
+
+ data.append((char)(FIN | QWebSocketProtocol::OpCodePing));
+ data.append(QChar::fromLatin1(0));
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+ dataProcessor.process(&buffer);
+ QCOMPARE(errorReceivedSpy.count(), 0);
+ QCOMPARE(textFrameReceivedSpy.count(), 0);
+ QCOMPARE(textMessageReceivedSpy.count(), 0);
+ QCOMPARE(binaryFrameReceivedSpy.count(), 0);
+ QCOMPARE(binaryMessageReceivedSpy.count(), 0);
+ QCOMPARE(closeFrameReceivedSpy.count(), 0);
+ QCOMPARE(pongReceivedSpy.count(), 0);
+ QCOMPARE(pingReceivedSpy.count(), 1);
+ buffer.close();
+
+ data.clear();
+ pingReceivedSpy.clear();
+ pongReceivedSpy.clear();
+ data.append((char)(FIN | QWebSocketProtocol::OpCodePong));
+ data.append(QChar::fromLatin1(0));
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+ dataProcessor.process(&buffer);
+ QCOMPARE(errorReceivedSpy.count(), 0);
+ QCOMPARE(textFrameReceivedSpy.count(), 0);
+ QCOMPARE(textMessageReceivedSpy.count(), 0);
+ QCOMPARE(binaryFrameReceivedSpy.count(), 0);
+ QCOMPARE(binaryMessageReceivedSpy.count(), 0);
+ QCOMPARE(closeFrameReceivedSpy.count(), 0);
+ QCOMPARE(pingReceivedSpy.count(), 0);
+ QCOMPARE(pongReceivedSpy.count(), 1);
+ buffer.close();
+}
+
+void tst_DataProcessor::goodCloseFrame_data()
+{
+ QTest::addColumn<QString>("payload");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("closeCode");
+ //control frame data cannot exceed 125 bytes; smaller than 124,
+ //because we also need a 2 byte close code
+ for (int i = 0; i < 124; ++i)
+ {
+ QTest::newRow(QStringLiteral("Close frame with %1 ASCII characters")
+ .arg(i).toLatin1().constData())
+ << QString(i, 'a')
+ << QWebSocketProtocol::CloseCodeNormal;
+ }
+ for (int i = 0; i < 126; ++i)
+ {
+ QTest::newRow(QStringLiteral("Text frame with containing ASCII character '0x%1'")
+ .arg(QByteArray(1, char(i)).toHex().constData()).toLatin1().constData())
+ << QString(1, char(i)) << QWebSocketProtocol::CloseCodeNormal;
+ }
+ QTest::newRow("Close frame with close code NORMAL")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCodeNormal;
+ QTest::newRow("Close frame with close code BAD OPERATION")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCodeBadOperation;
+ QTest::newRow("Close frame with close code DATATYPE NOT SUPPORTED")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCodeDatatypeNotSupported;
+ QTest::newRow("Close frame with close code GOING AWAY")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCodeGoingAway;
+ QTest::newRow("Close frame with close code MISSING EXTENSION")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCodeMissingExtension;
+ QTest::newRow("Close frame with close code POLICY VIOLATED")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCodePolicyViolated;
+ QTest::newRow("Close frame with close code PROTOCOL ERROR")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Close frame with close code TOO MUCH DATA")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCodeTooMuchData;
+ QTest::newRow("Close frame with close code WRONG DATATYPE")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCodeWrongDatatype;
+ QTest::newRow("Close frame with close code 3000")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCode(3000);
+ QTest::newRow("Close frame with close code 3999")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCode(3999);
+ QTest::newRow("Close frame with close code 4000")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCode(4000);
+ QTest::newRow("Close frame with close code 4999")
+ << QString(1, 'a') << QWebSocketProtocol::CloseCode(4999);
+
+ //close frames with no close reason
+ QTest::newRow("Close frame with close code NORMAL and no reason")
+ << QString() << QWebSocketProtocol::CloseCodeNormal;
+ QTest::newRow("Close frame with close code BAD OPERATION and no reason")
+ << QString() << QWebSocketProtocol::CloseCodeBadOperation;
+ QTest::newRow("Close frame with close code DATATYPE NOT SUPPORTED and no reason")
+ << QString() << QWebSocketProtocol::CloseCodeDatatypeNotSupported;
+ QTest::newRow("Close frame with close code GOING AWAY and no reason")
+ << QString() << QWebSocketProtocol::CloseCodeGoingAway;
+ QTest::newRow("Close frame with close code MISSING EXTENSION and no reason")
+ << QString() << QWebSocketProtocol::CloseCodeMissingExtension;
+ QTest::newRow("Close frame with close code POLICY VIOLATED and no reason")
+ << QString() << QWebSocketProtocol::CloseCodePolicyViolated;
+ QTest::newRow("Close frame with close code PROTOCOL ERROR and no reason")
+ << QString() << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Close frame with close code TOO MUCH DATA and no reason")
+ << QString() << QWebSocketProtocol::CloseCodeTooMuchData;
+ QTest::newRow("Close frame with close code WRONG DATATYPE and no reason")
+ << QString() << QWebSocketProtocol::CloseCodeWrongDatatype;
+ QTest::newRow("Close frame with close code 3000 and no reason")
+ << QString() << QWebSocketProtocol::CloseCode(3000);
+ QTest::newRow("Close frame with close code 3999 and no reason")
+ << QString() << QWebSocketProtocol::CloseCode(3999);
+ QTest::newRow("Close frame with close code 4000 and no reason")
+ << QString() << QWebSocketProtocol::CloseCode(4000);
+ QTest::newRow("Close frame with close code 4999 and no reason")
+ << QString() << QWebSocketProtocol::CloseCode(4999);
+
+ QTest::newRow("Close frame with no close code and no reason")
+ << QString() << QWebSocketProtocol::CloseCode(0);
+}
+
+void tst_DataProcessor::goodOpcodes_data()
+{
+ QTest::addColumn<QWebSocketProtocol::OpCode>("opCode");
+
+ QTest::newRow("Frame with PING opcode") << QWebSocketProtocol::OpCodePing;
+ QTest::newRow("Frame with PONG opcode") << QWebSocketProtocol::OpCodePong;
+ QTest::newRow("Frame with TEXT opcode") << QWebSocketProtocol::OpCodeText;
+ QTest::newRow("Frame with BINARY opcode") << QWebSocketProtocol::OpCodeBinary;
+ QTest::newRow("Frame with CLOSE opcode") << QWebSocketProtocol::OpCodeClose;
+}
+
+void tst_DataProcessor::goodOpcodes()
+{
+ QByteArray data;
+ QBuffer buffer;
+ QWebSocketDataProcessor dataProcessor;
+ QFETCH(QWebSocketProtocol::OpCode, opCode);
+
+ data.append((char)(FIN | opCode));
+ data.append(char(0)); //zero length
+
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+
+ QSignalSpy errorReceivedSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy closeReceivedSpy(&dataProcessor,
+ SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy pingReceivedSpy(&dataProcessor,
+ SIGNAL(pingReceived(QByteArray)));
+ QSignalSpy pongReceivedSpy(&dataProcessor,
+ SIGNAL(pongReceived(QByteArray)));
+ QSignalSpy textFrameReceivedSpy(&dataProcessor,
+ SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy textMessageReceivedSpy(&dataProcessor,
+ SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryFrameReceivedSpy(&dataProcessor,
+ SIGNAL(binaryFrameReceived(QByteArray,bool)));
+ QSignalSpy binaryMessageReceivedSpy(&dataProcessor,
+ SIGNAL(binaryMessageReceived(QByteArray)));
+
+ dataProcessor.process(&buffer);
+
+ QCOMPARE(errorReceivedSpy.count(), 0);
+ QCOMPARE(pingReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodePing ? 1 : 0);
+ QCOMPARE(pongReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodePong ? 1 : 0);
+ QCOMPARE(closeReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodeClose ? 1 : 0);
+ QCOMPARE(textFrameReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodeText ? 1 : 0);
+ QCOMPARE(textMessageReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodeText ? 1 : 0);
+ QCOMPARE(binaryFrameReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodeBinary ? 1 : 0);
+ QCOMPARE(binaryMessageReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodeBinary ? 1 : 0);
+
+ buffer.close();
+}
+
+void tst_DataProcessor::goodCloseFrame()
+{
+ QByteArray data;
+ QBuffer buffer;
+ QWebSocketDataProcessor dataProcessor;
+ QFETCH(QString, payload);
+ QFETCH(QWebSocketProtocol::CloseCode, closeCode);
+ quint16 swapped = qToBigEndian<quint16>(closeCode);
+ const char *wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+
+ data.append((char)(FIN | QWebSocketProtocol::OpCodeClose));
+ if (swapped != 0)
+ {
+ data.append(char(payload.length() + 2)).append(wireRepresentation, 2).append(payload);
+ }
+ else
+ {
+ data.append(QChar::fromLatin1(0)); //payload length 0;
+ //dataprocessor emits a CloseCodeNormal close code when none is present
+ closeCode = QWebSocketProtocol::CloseCodeNormal;
+ }
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+
+ QSignalSpy errorReceivedSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy pingReceivedSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray)));
+ QSignalSpy pongReceivedSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray)));
+ QSignalSpy closeFrameReceivedSpy(&dataProcessor,
+ SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy textFrameReceivedSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy textMessageReceivedSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryFrameReceivedSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+ QSignalSpy binaryMessageReceivedSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
+
+ dataProcessor.process(&buffer);
+
+ QCOMPARE(errorReceivedSpy.count(), 0);
+ QCOMPARE(pingReceivedSpy.count(), 0);
+ QCOMPARE(pongReceivedSpy.count(), 0);
+ QCOMPARE(closeFrameReceivedSpy.count(), 1);
+ QCOMPARE(textFrameReceivedSpy.count(), 0);
+ QCOMPARE(textMessageReceivedSpy.count(), 0);
+ QCOMPARE(binaryFrameReceivedSpy.count(), 0);
+ QCOMPARE(binaryMessageReceivedSpy.count(), 0);
+ QList<QVariant> arguments = closeFrameReceivedSpy.takeFirst();
+ QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), closeCode);
+ QCOMPARE(arguments.at(1).toString().length(), payload.length());
+ buffer.close();
+}
+
+void tst_DataProcessor::nonCharacterCodes_data()
+{
+ QTest::addColumn<quint8>("firstByte");
+ QTest::addColumn<quint8>("secondByte");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<bool>("isContinuationFrame");
+
+ nonCharacterSequence("efbfbe");
+ nonCharacterSequence("efbfbf");
+ nonCharacterSequence("f09fbfbe");
+ nonCharacterSequence("f09fbfbf");
+ nonCharacterSequence("f0afbfbe");
+ nonCharacterSequence("f0afbfbf");
+ nonCharacterSequence("f0bfbfbe");
+ nonCharacterSequence("f0bfbfbf");
+ nonCharacterSequence("f18fbfbe");
+ nonCharacterSequence("f18fbfbf");
+ nonCharacterSequence("f19fbfbe");
+ nonCharacterSequence("f19fbfbf");
+ nonCharacterSequence("f1afbfbe");
+ nonCharacterSequence("f1afbfbf");
+ nonCharacterSequence("f1bfbfbe");
+ nonCharacterSequence("f1bfbfbf");
+ nonCharacterSequence("f28fbfbe");
+ nonCharacterSequence("f28fbfbf");
+ nonCharacterSequence("f29fbfbe");
+ nonCharacterSequence("f29fbfbf");
+ nonCharacterSequence("f2afbfbe");
+ nonCharacterSequence("f2afbfbf");
+ nonCharacterSequence("f2bfbfbe");
+ nonCharacterSequence("f2bfbfbf");
+ nonCharacterSequence("f38fbfbe");
+ nonCharacterSequence("f38fbfbf");
+ nonCharacterSequence("f39fbfbe");
+ nonCharacterSequence("f39fbfbf");
+ nonCharacterSequence("f3afbfbe");
+ nonCharacterSequence("f3afbfbf");
+ nonCharacterSequence("f3bfbfbe");
+ nonCharacterSequence("f3bfbfbf");
+ nonCharacterSequence("f48fbfbe");
+ nonCharacterSequence("f48fbfbf");
+}
+
+void tst_DataProcessor::nonCharacterCodes()
+{
+ QFETCH(quint8, firstByte);
+ QFETCH(quint8, secondByte);
+ QFETCH(QByteArray, payload);
+ QFETCH(bool, isContinuationFrame);
+
+ if (!isContinuationFrame)
+ {
+ QByteArray data;
+ QBuffer buffer;
+ QWebSocketDataProcessor dataProcessor;
+ QSignalSpy errorSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy closeSpy(&dataProcessor,
+ SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy pingFrameSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray)));
+ QSignalSpy pongFrameSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray)));
+ QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+ QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
+
+ data.append(firstByte).append(secondByte);
+ data.append(payload);
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+ dataProcessor.process(&buffer);
+
+ QCOMPARE(errorSpy.count(), 0);
+ QCOMPARE(closeSpy.count(), 0);
+ QCOMPARE(pingFrameSpy.count(), 0);
+ QCOMPARE(pongFrameSpy.count(), 0);
+ QCOMPARE(textFrameSpy.count(), 1);
+ QCOMPARE(textMessageSpy.count(), 1);
+ QCOMPARE(binaryFrameSpy.count(), 0);
+ QCOMPARE(binaryMessageSpy.count(), 0);
+
+ QVariantList arguments = textFrameSpy.takeFirst();
+ QCOMPARE(arguments.at(0).value<QString>().toUtf8(), payload);
+ QCOMPARE(arguments.at(1).value<bool>(), !isContinuationFrame);
+ arguments = textMessageSpy.takeFirst();
+ QCOMPARE(arguments.at(0).value<QString>().toUtf8(), payload);
+ buffer.close();
+ }
+}
+
+void tst_DataProcessor::frameTooSmall()
+{
+ QByteArray data;
+ QBuffer buffer;
+ QWebSocketDataProcessor dataProcessor;
+ QByteArray firstFrame;
+
+ firstFrame.append(quint8(QWebSocketProtocol::OpCodeText)).append(char(1))
+ .append(QByteArray(1, 'a'));
+
+ //with nothing in the buffer, the dataProcessor should time out
+ //and the error should be CloseCodeGoingAway meaning the socket will be closed
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+ QSignalSpy errorSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy closeSpy(&dataProcessor,
+ SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy pingMessageSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray)));
+ QSignalSpy pongMessageSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray)));
+ QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
+ QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+
+ dataProcessor.process(&buffer);
+
+ QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(closeSpy.count(), 0);
+ QCOMPARE(pingMessageSpy.count(), 0);
+ QCOMPARE(pongMessageSpy.count(), 0);
+ QCOMPARE(textMessageSpy.count(), 0);
+ QCOMPARE(binaryMessageSpy.count(), 0);
+ QCOMPARE(textFrameSpy.count(), 0);
+ QCOMPARE(binaryFrameSpy.count(), 0);
+
+ QList<QVariant> arguments = errorSpy.takeFirst();
+ QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(),
+ QWebSocketProtocol::CloseCodeGoingAway);
+ errorSpy.clear();
+ closeSpy.clear();
+ pingMessageSpy.clear();
+ pongMessageSpy.clear();
+ textMessageSpy.clear();
+ binaryMessageSpy.clear();
+ textFrameSpy.clear();
+ binaryFrameSpy.clear();
+ buffer.close();
+ data.clear();
+
+ //only one byte; this is far too little;
+ //should get a time out as well and the error should be CloseCodeGoingAway
+ //meaning the socket will be closed
+ data.append(quint8('1')); //put 1 byte in the buffer; this is too little
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+
+ dataProcessor.process(&buffer);
+
+ QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(closeSpy.count(), 0);
+ QCOMPARE(pingMessageSpy.count(), 0);
+ QCOMPARE(pongMessageSpy.count(), 0);
+ QCOMPARE(textMessageSpy.count(), 0);
+ QCOMPARE(binaryMessageSpy.count(), 0);
+ QCOMPARE(textFrameSpy.count(), 0);
+ QCOMPARE(binaryFrameSpy.count(), 0);
+
+ arguments = errorSpy.takeFirst();
+ QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(),
+ QWebSocketProtocol::CloseCodeGoingAway);
+ buffer.close();
+ errorSpy.clear();
+ closeSpy.clear();
+ pingMessageSpy.clear();
+ pongMessageSpy.clear();
+ textMessageSpy.clear();
+ binaryMessageSpy.clear();
+ textFrameSpy.clear();
+ binaryFrameSpy.clear();
+ data.clear();
+
+
+ {
+ //text frame with final bit not set
+ data.append((char)(QWebSocketProtocol::OpCodeText)).append(char(0x0));
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+
+ dataProcessor.process(&buffer);
+
+ buffer.close();
+ data.clear();
+
+ //with nothing in the buffer,
+ //the dataProcessor should time out and the error should be CloseCodeGoingAway
+ //meaning the socket will be closed
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+ QSignalSpy errorSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ dataProcessor.process(&buffer);
+
+ QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(closeSpy.count(), 0);
+ QCOMPARE(pingMessageSpy.count(), 0);
+ QCOMPARE(pongMessageSpy.count(), 0);
+ QCOMPARE(textMessageSpy.count(), 0);
+ QCOMPARE(binaryMessageSpy.count(), 0);
+ QCOMPARE(textFrameSpy.count(), 1);
+ QCOMPARE(binaryFrameSpy.count(), 0);
+
+ QList<QVariant> arguments = errorSpy.takeFirst();
+ QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(),
+ QWebSocketProtocol::CloseCodeGoingAway);
+ errorSpy.clear();
+ closeSpy.clear();
+ pingMessageSpy.clear();
+ pongMessageSpy.clear();
+ textMessageSpy.clear();
+ binaryMessageSpy.clear();
+ textFrameSpy.clear();
+ binaryFrameSpy.clear();
+ buffer.close();
+ data.clear();
+
+ //text frame with final bit not set
+ data.append((char)(QWebSocketProtocol::OpCodeText)).append(char(0x0));
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+ dataProcessor.process(&buffer);
+
+ QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(closeSpy.count(), 0);
+ QCOMPARE(pingMessageSpy.count(), 0);
+ QCOMPARE(pongMessageSpy.count(), 0);
+ QCOMPARE(textMessageSpy.count(), 0);
+ QCOMPARE(binaryMessageSpy.count(), 0);
+ QCOMPARE(textFrameSpy.count(), 1);
+ QCOMPARE(binaryFrameSpy.count(), 0);
+
+ buffer.close();
+ data.clear();
+
+ errorSpy.clear();
+ closeSpy.clear();
+ pingMessageSpy.clear();
+ pongMessageSpy.clear();
+ textMessageSpy.clear();
+ binaryMessageSpy.clear();
+ textFrameSpy.clear();
+ binaryFrameSpy.clear();
+
+ //only 1 byte follows in continuation frame;
+ //should time out with close code CloseCodeGoingAway
+ data.append('a');
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+
+ dataProcessor.process(&buffer);
+ QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(closeSpy.count(), 0);
+ QCOMPARE(pingMessageSpy.count(), 0);
+ QCOMPARE(pongMessageSpy.count(), 0);
+ QCOMPARE(textMessageSpy.count(), 0);
+ QCOMPARE(binaryMessageSpy.count(), 0);
+ QCOMPARE(textFrameSpy.count(), 0);
+ QCOMPARE(binaryFrameSpy.count(), 0);
+ arguments = errorSpy.takeFirst();
+ QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(),
+ QWebSocketProtocol::CloseCodeGoingAway);
+ buffer.close();
+ }
+}
+
+void tst_DataProcessor::frameTooBig_data()
+{
+ QTest::addColumn<quint8>("firstByte");
+ QTest::addColumn<quint8>("secondByte");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<bool>("isContinuationFrame");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedCloseCode");
+
+ quint64 swapped64 = 0;
+ const char *wireRepresentation = 0;
+
+ //only data frames are checked for being too big
+ //control frames have explicit checking on a maximum payload size of 125,
+ //which is tested elsewhere
+
+ swapped64 = qToBigEndian<quint64>(QWebSocketDataProcessor::maxFrameSize() + 1);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped64));
+ QTest::newRow("Text frame with payload size > INT_MAX")
+ << quint8(FIN | QWebSocketProtocol::OpCodeText)
+ << quint8(127)
+ << QByteArray(wireRepresentation, 8).append(QByteArray(32, 'a'))
+ << false
+ << QWebSocketProtocol::CloseCodeTooMuchData;
+
+ swapped64 = qToBigEndian<quint64>(QWebSocketDataProcessor::maxFrameSize() + 1);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped64));
+ QTest::newRow("Binary frame with payload size > INT_MAX")
+ << quint8(FIN | QWebSocketProtocol::OpCodeBinary)
+ << quint8(127)
+ << QByteArray(wireRepresentation, 8).append(QByteArray(32, 'a'))
+ << false
+ << QWebSocketProtocol::CloseCodeTooMuchData;
+
+ swapped64 = qToBigEndian<quint64>(QWebSocketDataProcessor::maxFrameSize() + 1);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped64));
+ QTest::newRow("Continuation frame with payload size > INT_MAX")
+ << quint8(FIN | QWebSocketProtocol::OpCodeContinue)
+ << quint8(127)
+ << QByteArray(wireRepresentation, 8).append(QByteArray(32, 'a'))
+ << true
+ << QWebSocketProtocol::CloseCodeTooMuchData;
+}
+
+void tst_DataProcessor::frameTooBig()
+{
+ doTest();
+}
+
+void tst_DataProcessor::invalidHeader_data()
+{
+ //The first byte contain the FIN, RSV1, RSV2, RSV3 and the Opcode
+ //The second byte contains the MaskFlag and the length of the frame
+ QTest::addColumn<quint8>("firstByte");
+ QTest::addColumn<quint8>("secondByte");
+ //superfluous, but present to be able to call doTest(), which expects a payload field
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<bool>("isContinuationFrame");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedCloseCode");
+
+ //invalid bit fields
+ invalidField("RSV1 set", RSV1);
+ invalidField("RSV2 set", RSV2);
+ invalidField("RSV3 set", RSV3);
+ invalidField("RSV1 and RSV2 set", RSV1 | RSV2);
+ invalidField("RSV1 and RSV3 set", RSV1 | RSV3);
+ invalidField("RSV2 and RSV3 set", RSV2 | RSV3);
+ invalidField("RSV1, RSV2 and RSV3 set", RSV1 | RSV2 | RSV3);
+
+ //invalid opcodes
+ invalidField("Invalid OpCode 3", QWebSocketProtocol::OpCodeReserved3);
+ invalidField("Invalid OpCode 4", QWebSocketProtocol::OpCodeReserved4);
+ invalidField("Invalid OpCode 5", QWebSocketProtocol::OpCodeReserved5);
+ invalidField("Invalid OpCode 6", QWebSocketProtocol::OpCodeReserved6);
+ invalidField("Invalid OpCode 7", QWebSocketProtocol::OpCodeReserved7);
+ invalidField("Invalid OpCode B", QWebSocketProtocol::OpCodeReservedB);
+ invalidField("Invalid OpCode C", QWebSocketProtocol::OpCodeReservedC);
+ invalidField("Invalid OpCode D", QWebSocketProtocol::OpCodeReservedD);
+ invalidField("Invalid OpCode E", QWebSocketProtocol::OpCodeReservedE);
+ invalidField("Invalid OpCode F", QWebSocketProtocol::OpCodeReservedF);
+}
+
+void tst_DataProcessor::invalidHeader()
+{
+ doTest();
+}
+
+void tst_DataProcessor::invalidControlFrame_data()
+{
+ QTest::addColumn<quint8>("firstByte");
+ QTest::addColumn<quint8>("secondByte");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<bool>("isContinuationFrame");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedCloseCode");
+
+
+ QTest::newRow("Close control frame with payload size 126")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(126)
+ << QByteArray()
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Ping control frame with payload size 126")
+ << quint8(FIN | QWebSocketProtocol::OpCodePing)
+ << quint8(126)
+ << QByteArray()
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Close control frame with payload size 126")
+ << quint8(FIN | QWebSocketProtocol::OpCodePong)
+ << quint8(126)
+ << QByteArray()
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+
+ QTest::newRow("Non-final close control frame (fragmented)")
+ << quint8(QWebSocketProtocol::OpCodeClose)
+ << quint8(32)
+ << QByteArray()
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Non-final ping control frame (fragmented)")
+ << quint8(QWebSocketProtocol::OpCodePing)
+ << quint8(32) << QByteArray()
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Non-final pong control frame (fragmented)")
+ << quint8(QWebSocketProtocol::OpCodePong)
+ << quint8(32)
+ << QByteArray()
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+}
+
+void tst_DataProcessor::invalidControlFrame()
+{
+ doTest();
+}
+
+void tst_DataProcessor::invalidCloseFrame_data()
+{
+ QTest::addColumn<quint8>("firstByte");
+ QTest::addColumn<quint8>("secondByte");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<bool>("isContinuationFrame");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedCloseCode");
+
+ QTest::newRow("Close control frame with payload size 1")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(1)
+ << QByteArray(1, 'a')
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ quint16 swapped = qToBigEndian<quint16>(QWebSocketProtocol::CloseCodeAbnormalDisconnection);
+ const char *wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+
+ //Not allowed per RFC 6455 (see para 7.4.1)
+ QTest::newRow("Close control frame close code ABNORMAL DISCONNECTION")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(QWebSocketProtocol::CloseCodeMissingStatusCode);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ //Not allowed per RFC 6455 (see para 7.4.1)
+ QTest::newRow("Close control frame close code MISSING STATUS CODE")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(1004);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 1004")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(QWebSocketProtocol::CloseCodeTlsHandshakeFailed);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ //Not allowed per RFC 6455 (see para 7.4.1)
+ QTest::newRow("Close control frame close code TLS HANDSHAKE FAILED")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(0);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 0")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(999);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 999")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(1012);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 1012")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(1013);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 1013")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(1014);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 1014")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(1100);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 1100")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(2000);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 2000")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(2999);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 2999")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(5000);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 5000")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ swapped = qToBigEndian<quint16>(65535u);
+ wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped));
+ QTest::newRow("Close control frame close code 65535")
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(2)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+}
+
+void tst_DataProcessor::invalidCloseFrame()
+{
+ doCloseFrameTest();
+}
+
+void tst_DataProcessor::minimumSizeRequirement_data()
+{
+ QTest::addColumn<quint8>("firstByte");
+ QTest::addColumn<quint8>("secondByte");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<bool>("isContinuationFrame");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedCloseCode");
+
+ minimumSize16Bit(0);
+ minimumSize16Bit(64);
+ minimumSize16Bit(125);
+
+ minimumSize64Bit(0);
+ minimumSize64Bit(64);
+ minimumSize64Bit(125);
+ minimumSize64Bit(126);
+ minimumSize64Bit(256);
+ minimumSize64Bit(512);
+ minimumSize64Bit(1024);
+ minimumSize64Bit(2048);
+ minimumSize64Bit(4096);
+ minimumSize64Bit(8192);
+ minimumSize64Bit(16384);
+ minimumSize64Bit(32768);
+ minimumSize64Bit(0xFFFFu);
+}
+
+void tst_DataProcessor::minimumSizeRequirement()
+{
+ doTest();
+}
+
+void tst_DataProcessor::invalidPayload_data(bool isControlFrame)
+{
+ QTest::addColumn<quint8>("firstByte");
+ QTest::addColumn<quint8>("secondByte");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<bool>("isContinuationFrame");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedCloseCode");
+
+ //6.3: invalid UTF-8 sequence
+ invalidUTF8("case 6.3.1", "cebae1bdb9cf83cebcceb5eda080656469746564", isControlFrame);
+
+ //6.4.: fail fast tests; invalid UTF-8 in middle of string
+ invalidUTF8("case 6.4.1", "cebae1bdb9cf83cebcceb5f4908080656469746564", isControlFrame);
+ invalidUTF8("case 6.4.4", "cebae1bdb9cf83cebcceb5eda080656469746564", isControlFrame);
+
+ //6.6: All prefixes of a valid UTF-8 string that contains multi-byte code points
+ invalidUTF8("case 6.6.1", "ce", isControlFrame);
+ invalidUTF8("case 6.6.3", "cebae1", isControlFrame);
+ invalidUTF8("case 6.6.4", "cebae1bd", isControlFrame);
+ invalidUTF8("case 6.6.6", "cebae1bdb9cf", isControlFrame);
+ invalidUTF8("case 6.6.8", "cebae1bdb9cf83ce", isControlFrame);
+ invalidUTF8("case 6.6.10", "cebae1bdb9cf83cebcce", isControlFrame);
+
+ //6.8: First possible sequence length 5/6 (invalid codepoints)
+ invalidUTF8("case 6.8.1", "f888808080", isControlFrame);
+ invalidUTF8("case 6.8.2", "fc8480808080", isControlFrame);
+
+ //6.10: Last possible sequence length 4/5/6 (invalid codepoints)
+ invalidUTF8("case 6.10.1", "f7bfbfbf", isControlFrame);
+ invalidUTF8("case 6.10.2", "fbbfbfbfbf", isControlFrame);
+ invalidUTF8("case 6.10.3", "fdbfbfbfbfbf", isControlFrame);
+
+ //5.11: boundary conditions
+ invalidUTF8("case 6.11.5", "f4908080", isControlFrame);
+
+ //6.12: unexpected continuation bytes
+ invalidUTF8("case 6.12.1", "80", isControlFrame);
+ invalidUTF8("case 6.12.2", "bf", isControlFrame);
+ invalidUTF8("case 6.12.3", "80bf", isControlFrame);
+ invalidUTF8("case 6.12.4", "80bf80", isControlFrame);
+ invalidUTF8("case 6.12.5", "80bf80bf", isControlFrame);
+ invalidUTF8("case 6.12.6", "80bf80bf80", isControlFrame);
+ invalidUTF8("case 6.12.7", "80bf80bf80bf", isControlFrame);
+ invalidUTF8("case 6.12.8",
+ "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a"
+ "7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbe",
+ isControlFrame);
+
+ //6.13: lonely start characters
+ invalidUTF8("case 6.13.1",
+ "c020c120c220c320c420c520c620c720c820c920ca20cb20cc20cd20ce20cf20d020d120d220"
+ "d320d420d520d620d720d820d920da20db20dc20dd20de20",
+ isControlFrame);
+ invalidUTF8("case 6.13.2", "e020e120e220e320e420e520e620e720e820e920ea20eb20ec20ed20ee20",
+ isControlFrame);
+ invalidUTF8("case 6.13.3", "f020f120f220f320f420f520f620", isControlFrame);
+ invalidUTF8("case 6.13.4", "f820f920fa20", isControlFrame);
+ invalidUTF8("case 6.13.5", "fc20", isControlFrame);
+
+ //6.14: sequences with last continuation byte missing
+ invalidUTF8("case 6.14.1", "c0", isControlFrame);
+ invalidUTF8("case 6.14.2", "e080", isControlFrame);
+ invalidUTF8("case 6.14.3", "f08080", isControlFrame);
+ invalidUTF8("case 6.14.4", "f8808080", isControlFrame);
+ invalidUTF8("case 6.14.5", "fc80808080", isControlFrame);
+ invalidUTF8("case 6.14.6", "df", isControlFrame);
+ invalidUTF8("case 6.14.7", "efbf", isControlFrame);
+ invalidUTF8("case 6.14.8", "f7bfbf", isControlFrame);
+ invalidUTF8("case 6.14.9", "fbbfbfbf", isControlFrame);
+ invalidUTF8("case 6.14.10", "fdbfbfbfbf", isControlFrame);
+
+ //6.15: concatenation of incomplete sequences
+ invalidUTF8("case 6.15.1",
+ "c0e080f08080f8808080fc80808080dfefbff7bfbffbbfbfbffdbfbfbfbf", isControlFrame);
+
+ //6.16: impossible bytes
+ invalidUTF8("case 6.16.1", "fe", isControlFrame);
+ invalidUTF8("case 6.16.2", "ff", isControlFrame);
+ invalidUTF8("case 6.16.3", "fefeffff", isControlFrame);
+
+ //6.17: overlong ASCII characters
+ invalidUTF8("case 6.17.1", "c0af", isControlFrame);
+ invalidUTF8("case 6.17.2", "e080af", isControlFrame);
+ invalidUTF8("case 6.17.3", "f08080af", isControlFrame);
+ invalidUTF8("case 6.17.4", "f8808080af", isControlFrame);
+ invalidUTF8("case 6.17.5", "fc80808080af", isControlFrame);
+
+ //6.18: maximum overlong sequences
+ invalidUTF8("case 6.18.1", "c1bf", isControlFrame);
+ invalidUTF8("case 6.18.2", "e09fbf", isControlFrame);
+ invalidUTF8("case 6.18.3", "f08fbfbf", isControlFrame);
+ invalidUTF8("case 6.18.4", "f887bfbfbf", isControlFrame);
+ invalidUTF8("case 6.18.5", "fc83bfbfbfbf", isControlFrame);
+
+ //6.19: overlong presentation of the NUL character
+ invalidUTF8("case 6.19.1", "c080", isControlFrame);
+ invalidUTF8("case 6.19.2", "e08080", isControlFrame);
+ invalidUTF8("case 6.19.3", "f0808080", isControlFrame);
+ invalidUTF8("case 6.19.4", "f880808080", isControlFrame);
+ invalidUTF8("case 6.19.5", "fc8080808080", isControlFrame);
+
+ //6.20: Single UTF-16 surrogates
+ invalidUTF8("case 6.20.1", "eda080", isControlFrame);
+ invalidUTF8("case 6.20.2", "edadbf", isControlFrame);
+ invalidUTF8("case 6.20.3", "edae80", isControlFrame);
+ invalidUTF8("case 6.20.4", "edafbf", isControlFrame);
+ invalidUTF8("case 6.20.5", "edb080", isControlFrame);
+ invalidUTF8("case 6.20.6", "edbe80", isControlFrame);
+ invalidUTF8("case 6.20.7", "edbfbf", isControlFrame);
+
+ //6.21: Paired UTF-16 surrogates
+ invalidUTF8("case 6.21.1", "eda080edb080", isControlFrame);
+ invalidUTF8("case 6.21.2", "eda080edbfbf", isControlFrame);
+ invalidUTF8("case 6.21.3", "edadbfedb080", isControlFrame);
+ invalidUTF8("case 6.21.4", "edadbfedbfbf", isControlFrame);
+ invalidUTF8("case 6.21.5", "edae80edb080", isControlFrame);
+ invalidUTF8("case 6.21.6", "edae80edbfbf", isControlFrame);
+ invalidUTF8("case 6.21.7", "edafbfedb080", isControlFrame);
+ invalidUTF8("case 6.21.8", "edafbfedbfbf", isControlFrame);
+}
+
+void tst_DataProcessor::invalidPayload()
+{
+ doTest();
+}
+
+void tst_DataProcessor::invalidPayloadInCloseFrame_data()
+{
+ invalidPayload_data(true);
+}
+
+void tst_DataProcessor::invalidPayloadInCloseFrame()
+{
+ QFETCH(quint8, firstByte);
+ QFETCH(quint8, secondByte);
+ QFETCH(QByteArray, payload);
+ QFETCH(bool, isContinuationFrame);
+ QFETCH(QWebSocketProtocol::CloseCode, expectedCloseCode);
+
+ Q_UNUSED(isContinuationFrame)
+
+ QByteArray data;
+ QBuffer buffer;
+ QWebSocketDataProcessor dataProcessor;
+ QSignalSpy closeSpy(&dataProcessor,
+ SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy errorSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy pingMessageSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray)));
+ QSignalSpy pongMessageSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray)));
+ QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
+ QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+
+ data.append(firstByte).append(secondByte);
+ data.append(payload);
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+ dataProcessor.process(&buffer);
+ QCOMPARE(closeSpy.count(), 1);
+ QCOMPARE(errorSpy.count(), 0);
+ QCOMPARE(pingMessageSpy.count(), 0);
+ QCOMPARE(pongMessageSpy.count(), 0);
+ QCOMPARE(textMessageSpy.count(), 0);
+ QCOMPARE(binaryMessageSpy.count(), 0);
+ QCOMPARE(textFrameSpy.count(), 0);
+ QCOMPARE(binaryFrameSpy.count(), 0);
+ QVariantList arguments = closeSpy.takeFirst();
+ QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode);
+ buffer.close();
+}
+
+void tst_DataProcessor::incompletePayload_data()
+{
+ QTest::addColumn<quint8>("firstByte");
+ QTest::addColumn<quint8>("secondByte");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<bool>("isContinuationFrame");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedCloseCode");
+
+ incompleteFrame(QWebSocketProtocol::OpCodeText, 125, 0);
+ incompleteFrame(QWebSocketProtocol::OpCodeText, 64, 32);
+ incompleteFrame(QWebSocketProtocol::OpCodeText, 256, 32);
+ incompleteFrame(QWebSocketProtocol::OpCodeText, 128000, 32);
+ incompleteFrame(QWebSocketProtocol::OpCodeBinary, 125, 0);
+ incompleteFrame(QWebSocketProtocol::OpCodeBinary, 64, 32);
+ incompleteFrame(QWebSocketProtocol::OpCodeBinary, 256, 32);
+ incompleteFrame(QWebSocketProtocol::OpCodeBinary, 128000, 32);
+ incompleteFrame(QWebSocketProtocol::OpCodeContinue, 125, 0);
+ incompleteFrame(QWebSocketProtocol::OpCodeContinue, 64, 32);
+ incompleteFrame(QWebSocketProtocol::OpCodeContinue, 256, 32);
+ incompleteFrame(QWebSocketProtocol::OpCodeContinue, 128000, 32);
+
+ incompleteFrame(QWebSocketProtocol::OpCodeClose, 64, 32);
+ incompleteFrame(QWebSocketProtocol::OpCodePing, 64, 32);
+ incompleteFrame(QWebSocketProtocol::OpCodePong, 64, 32);
+}
+
+void tst_DataProcessor::incompletePayload()
+{
+ doTest();
+}
+
+void tst_DataProcessor::incompleteSizeField_data()
+{
+ QTest::addColumn<quint8>("firstByte");
+ QTest::addColumn<quint8>("secondByte");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<bool>("isContinuationFrame");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedCloseCode");
+
+ //for a frame length value of 126
+ //there should be 2 bytes following to form a 16-bit frame length
+ insertIncompleteSizeFieldTest(126, 0);
+ insertIncompleteSizeFieldTest(126, 1);
+
+ //for a frame length value of 127
+ //there should be 8 bytes following to form a 64-bit frame length
+ insertIncompleteSizeFieldTest(127, 0);
+ insertIncompleteSizeFieldTest(127, 1);
+ insertIncompleteSizeFieldTest(127, 2);
+ insertIncompleteSizeFieldTest(127, 3);
+ insertIncompleteSizeFieldTest(127, 4);
+ insertIncompleteSizeFieldTest(127, 5);
+ insertIncompleteSizeFieldTest(127, 6);
+ insertIncompleteSizeFieldTest(127, 7);
+}
+
+void tst_DataProcessor::incompleteSizeField()
+{
+ doTest();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+/// HELPER FUNCTIONS
+//////////////////////////////////////////////////////////////////////////////////////////
+void tst_DataProcessor::doTest()
+{
+ QFETCH(quint8, firstByte);
+ QFETCH(quint8, secondByte);
+ QFETCH(QByteArray, payload);
+ QFETCH(bool, isContinuationFrame);
+ QFETCH(QWebSocketProtocol::CloseCode, expectedCloseCode);
+
+ QByteArray data;
+ QBuffer buffer;
+ QWebSocketDataProcessor dataProcessor;
+ QSignalSpy errorSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
+ QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+
+ if (isContinuationFrame)
+ {
+ data.append(quint8(QWebSocketProtocol::OpCodeText))
+ .append(char(1))
+ .append(QByteArray(1, 'a'));
+ }
+ data.append(firstByte).append(secondByte);
+ data.append(payload);
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+ dataProcessor.process(&buffer);
+ QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(textMessageSpy.count(), 0);
+ QCOMPARE(binaryMessageSpy.count(), 0);
+ QCOMPARE(textFrameSpy.count(), isContinuationFrame ? 1 : 0);
+ QCOMPARE(binaryFrameSpy.count(), 0);
+ QVariantList arguments = errorSpy.takeFirst();
+ QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode);
+ buffer.close();
+ errorSpy.clear();
+ data.clear();
+}
+
+void tst_DataProcessor::doCloseFrameTest()
+{
+ QFETCH(quint8, firstByte);
+ QFETCH(quint8, secondByte);
+ QFETCH(QByteArray, payload);
+ QFETCH(bool, isContinuationFrame);
+ QFETCH(QWebSocketProtocol::CloseCode, expectedCloseCode);
+
+ Q_UNUSED(isContinuationFrame)
+
+ QByteArray data;
+ QBuffer buffer;
+ QWebSocketDataProcessor dataProcessor;
+ QSignalSpy closeSpy(&dataProcessor,
+ SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy errorSpy(&dataProcessor,
+ SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)));
+ QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)));
+ QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+
+ data.append(firstByte).append(secondByte);
+ data.append(payload);
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+ dataProcessor.process(&buffer);
+ QCOMPARE(closeSpy.count(), 1);
+ QCOMPARE(errorSpy.count(), 0);
+ QCOMPARE(textMessageSpy.count(), 0);
+ QCOMPARE(binaryMessageSpy.count(), 0);
+ QCOMPARE(textFrameSpy.count(), 0);
+ QCOMPARE(binaryFrameSpy.count(), 0);
+ QVariantList arguments = closeSpy.takeFirst();
+ QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode);
+ buffer.close();
+}
+
+QString tst_DataProcessor::opCodeToString(quint8 opCode)
+{
+ QString frameType;
+ switch (opCode)
+ {
+ case QWebSocketProtocol::OpCodeBinary:
+ {
+ frameType = QStringLiteral("Binary");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeText:
+ {
+ frameType = QStringLiteral("Text");
+ break;
+ }
+ case QWebSocketProtocol::OpCodePing:
+ {
+ frameType = QStringLiteral("Ping");
+ break;
+ }
+ case QWebSocketProtocol::OpCodePong:
+ {
+ frameType = QStringLiteral("Pong");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeClose:
+ {
+ frameType = QStringLiteral("Close");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeContinue:
+ {
+ frameType = QStringLiteral("Continuation");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeReserved3:
+ {
+ frameType = QStringLiteral("Reserved3");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeReserved4:
+ {
+ frameType = QStringLiteral("Reserved5");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeReserved5:
+ {
+ frameType = QStringLiteral("Reserved5");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeReserved6:
+ {
+ frameType = QStringLiteral("Reserved6");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeReserved7:
+ {
+ frameType = QStringLiteral("Reserved7");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeReservedB:
+ {
+ frameType = QStringLiteral("ReservedB");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeReservedC:
+ {
+ frameType = QStringLiteral("ReservedC");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeReservedD:
+ {
+ frameType = QStringLiteral("ReservedD");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeReservedE:
+ {
+ frameType = QStringLiteral("ReservedE");
+ break;
+ }
+ case QWebSocketProtocol::OpCodeReservedF:
+ {
+ frameType = QStringLiteral("ReservedF");
+ break;
+ }
+ default:
+ {
+ //should never come here
+ Q_ASSERT(false);
+ }
+ }
+ return frameType;
+}
+
+void tst_DataProcessor::minimumSize16Bit(quint16 sizeInBytes)
+{
+ quint16 swapped16 = qToBigEndian<quint16>(sizeInBytes);
+ const char *wireRepresentation
+ = static_cast<const char *>(static_cast<const void *>(&swapped16));
+ QTest::newRow(QStringLiteral("Text frame with payload size %1, represented in 2 bytes")
+ .arg(sizeInBytes).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeText)
+ << quint8(126)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow(QStringLiteral("Binary frame with payload size %1, represented in 2 bytes")
+ .arg(sizeInBytes).toLatin1().constBegin())
+ << quint8(FIN | QWebSocketProtocol::OpCodeBinary)
+ << quint8(126)
+ << QByteArray(wireRepresentation, 2)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow(QStringLiteral("Continuation frame with payload size %1, represented in 2 bytes")
+ .arg(sizeInBytes).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeContinue)
+ << quint8(126)
+ << QByteArray(wireRepresentation, 2)
+ << true
+ << QWebSocketProtocol::CloseCodeProtocolError;
+}
+
+void tst_DataProcessor::minimumSize64Bit(quint64 sizeInBytes)
+{
+ quint64 swapped64 = qToBigEndian<quint64>(sizeInBytes);
+ const char *wireRepresentation
+ = static_cast<const char *>(static_cast<const void *>(&swapped64));
+
+ QTest::newRow(QStringLiteral("Text frame with payload size %1, represented in 8 bytes")
+ .arg(sizeInBytes).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeText)
+ << quint8(127)
+ << QByteArray(wireRepresentation, 8)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+
+ QTest::newRow(QStringLiteral("Binary frame with payload size %1, represented in 8 bytes")
+ .arg(sizeInBytes).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeBinary)
+ << quint8(127)
+ << QByteArray(wireRepresentation, 8)
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+
+ QTest::newRow(QStringLiteral("Continuation frame with payload size %1, represented in 8 bytes")
+ .arg(sizeInBytes).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeContinue)
+ << quint8(127)
+ << QByteArray(wireRepresentation, 8)
+ << true
+ << QWebSocketProtocol::CloseCodeProtocolError;
+}
+
+void tst_DataProcessor::invalidUTF8(const char *dataTag, const char *utf8Sequence,
+ bool isCloseFrame)
+{
+ QByteArray payload = QByteArray::fromHex(utf8Sequence);
+
+ if (isCloseFrame)
+ {
+ quint16 closeCode = qToBigEndian<quint16>(QWebSocketProtocol::CloseCodeNormal);
+ const char *wireRepresentation
+ = static_cast<const char *>(static_cast<const void *>(&closeCode));
+ QTest::newRow(QStringLiteral("Close frame with invalid UTF8-sequence: %1")
+ .arg(dataTag).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeClose)
+ << quint8(payload.length() + 2)
+ << QByteArray(wireRepresentation, 2).append(payload)
+ << false
+ << QWebSocketProtocol::CloseCodeWrongDatatype;
+ }
+ else
+ {
+ QTest::newRow(QStringLiteral("Text frame with invalid UTF8-sequence: %1")
+ .arg(dataTag).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeText)
+ << quint8(payload.length())
+ << payload
+ << false
+ << QWebSocketProtocol::CloseCodeWrongDatatype;
+
+ QTest::newRow(QStringLiteral("Continuation text frame with invalid UTF8-sequence: %1")
+ .arg(dataTag).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeContinue)
+ << quint8(payload.length())
+ << payload
+ << true
+ << QWebSocketProtocol::CloseCodeWrongDatatype;
+ }
+}
+
+void tst_DataProcessor::invalidField(const char *dataTag, quint8 invalidFieldValue)
+{
+ QTest::newRow(dataTag) << quint8(FIN | invalidFieldValue)
+ << quint8(0x00)
+ << QByteArray()
+ << false
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow(QString::fromLatin1(dataTag).append(QStringLiteral(" with continuation frame"))
+ .toLatin1().constData())
+ << quint8(FIN | invalidFieldValue)
+ << quint8(0x00)
+ << QByteArray()
+ << true
+ << QWebSocketProtocol::CloseCodeProtocolError;
+}
+
+void tst_DataProcessor::incompleteFrame(quint8 controlCode, quint64 indicatedSize,
+ quint64 actualPayloadSize)
+{
+ QVERIFY(!QWebSocketProtocol::isOpCodeReserved(QWebSocketProtocol::OpCode(controlCode)));
+ QVERIFY(indicatedSize > actualPayloadSize);
+
+ QString frameType = opCodeToString(controlCode);
+ QByteArray firstFrame;
+
+ if (indicatedSize < 126)
+ {
+ //doing extensive QStringLiteral concatenations here, because
+ //MSVC 2010 complains when using concatenation literal strings about
+ //concatenation of wide and narrow strings (error C2308)
+ QTest::newRow(frameType
+ .append(QStringLiteral(" frame with payload size %1, but only %2 bytes") +
+ QStringLiteral(" of data")
+ .arg(indicatedSize).arg(actualPayloadSize)).toLatin1().constData())
+ << quint8(FIN | controlCode)
+ << quint8(indicatedSize)
+ << firstFrame.append(QByteArray(actualPayloadSize, 'a'))
+ << (controlCode == QWebSocketProtocol::OpCodeContinue)
+ << QWebSocketProtocol::CloseCodeGoingAway;
+ }
+ else if (indicatedSize <= 0xFFFFu)
+ {
+ quint16 swapped16 = qToBigEndian<quint16>(static_cast<quint16>(indicatedSize));
+ const char *wireRepresentation
+ = static_cast<const char *>(static_cast<const void *>(&swapped16));
+ QTest::newRow(
+ frameType.append(QStringLiteral(" frame with payload size %1, but only ") +
+ QStringLiteral("%2 bytes of data")
+ .arg(indicatedSize)
+ .arg(actualPayloadSize)).toLatin1().constData())
+ << quint8(FIN | controlCode)
+ << quint8(126)
+ << firstFrame.append(QByteArray(wireRepresentation, 2)
+ .append(QByteArray(actualPayloadSize, 'a')))
+ << (controlCode == QWebSocketProtocol::OpCodeContinue)
+ << QWebSocketProtocol::CloseCodeGoingAway;
+ }
+ else
+ {
+ quint64 swapped64 = qToBigEndian<quint64>(indicatedSize);
+ const char *wireRepresentation
+ = static_cast<const char *>(static_cast<const void *>(&swapped64));
+ QTest::newRow(frameType
+ .append(QStringLiteral(" frame with payload size %1, but only %2 bytes ") +
+ QStringLiteral("of data")
+ .arg(indicatedSize).arg(actualPayloadSize)).toLatin1().constData())
+ << quint8(FIN | controlCode)
+ << quint8(127)
+ << firstFrame.append(QByteArray(wireRepresentation, 8)
+ .append(QByteArray(actualPayloadSize, 'a')))
+ << (controlCode == QWebSocketProtocol::OpCodeContinue)
+ << QWebSocketProtocol::CloseCodeGoingAway;
+ }
+}
+
+void tst_DataProcessor::nonCharacterSequence(const char *sequence)
+{
+ QByteArray utf8Sequence = QByteArray::fromHex(sequence);
+
+ //doing extensive QStringLiteral concatenations here, because
+ //MSVC 2010 complains when using concatenation literal strings about
+ //concatenation of wide and narrow strings (error C2308)
+ QTest::newRow((QStringLiteral("Text frame with payload containing the non-control character ") +
+ QStringLiteral("sequence 0x%1"))
+ .arg(QString::fromLocal8Bit(sequence)).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeText)
+ << quint8(utf8Sequence.size())
+ << utf8Sequence
+ << false;
+
+ QTest::newRow((QStringLiteral("Continuation frame with payload containing the non-control ") +
+ QStringLiteral("character sequence 0x%1"))
+ .arg(QString::fromLocal8Bit(sequence)).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeContinue)
+ << quint8(utf8Sequence.size())
+ << utf8Sequence
+ << true;
+}
+
+void tst_DataProcessor::insertIncompleteSizeFieldTest(quint8 payloadCode, quint8 numBytesFollowing)
+{
+ //doing extensive QStringLiteral concatenations here, because
+ //MSVC 2010 complains when using concatenation literal strings about
+ //concatenation of wide and narrow strings (error C2308)
+ QTest::newRow(QStringLiteral("Text frame with payload size %1, with %2 bytes following.")
+ .arg(payloadCode).arg(numBytesFollowing).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeText)
+ << quint8(payloadCode)
+ << QByteArray(numBytesFollowing, quint8(1))
+ << false
+ << QWebSocketProtocol::CloseCodeGoingAway;
+ QTest::newRow(QStringLiteral("Binary frame with payload size %1, with %2 bytes following.")
+ .arg(payloadCode).arg(numBytesFollowing).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeBinary)
+ << quint8(payloadCode)
+ << QByteArray(numBytesFollowing, quint8(1))
+ << false
+ << QWebSocketProtocol::CloseCodeGoingAway;
+ QTest::newRow((QStringLiteral("Continuation frame with payload size %1, with %2 bytes ") +
+ QStringLiteral("following."))
+ .arg(payloadCode).arg(numBytesFollowing).toLatin1().constData())
+ << quint8(FIN | QWebSocketProtocol::OpCodeContinue)
+ << quint8(payloadCode)
+ << QByteArray(numBytesFollowing, quint8(1))
+ << true
+ << QWebSocketProtocol::CloseCodeGoingAway;
+}
+
+QTEST_MAIN(tst_DataProcessor)
+
+#include "tst_dataprocessor.moc"
diff --git a/tests/auto/websockets/handshakerequest/handshakerequest.pro b/tests/auto/websockets/handshakerequest/handshakerequest.pro
new file mode 100644
index 0000000..d887cd6
--- /dev/null
+++ b/tests/auto/websockets/handshakerequest/handshakerequest.pro
@@ -0,0 +1,14 @@
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+TARGET = tst_handshakerequest
+
+QT = core testlib websockets websockets-private
+
+SOURCES += tst_handshakerequest.cpp
+
+requires(contains(QT_CONFIG, private_tests))
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/websockets/handshakerequest/tst_handshakerequest.cpp b/tests/auto/websockets/handshakerequest/tst_handshakerequest.cpp
new file mode 100644
index 0000000..9c579bd
--- /dev/null
+++ b/tests/auto/websockets/handshakerequest/tst_handshakerequest.cpp
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QtTest/qtestcase.h>
+#include <QtCore/QDebug>
+#include <QtCore/QByteArray>
+#include <QtCore/QtEndian>
+
+#include "private/qwebsockethandshakerequest_p.h"
+#include "private/qwebsocketprotocol_p.h"
+#include "QtWebSockets/qwebsocketprotocol.h"
+
+QT_USE_NAMESPACE
+
+Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
+Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
+
+class tst_HandshakeRequest : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_HandshakeRequest();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void tst_initialization();
+
+ void tst_invalidStream_data();
+ void tst_invalidStream();
+
+ void tst_multipleValuesInConnectionHeader();
+ void tst_multipleVersions();
+
+ void tst_qtbug_39355();
+};
+
+tst_HandshakeRequest::tst_HandshakeRequest()
+{}
+
+void tst_HandshakeRequest::initTestCase()
+{
+}
+
+void tst_HandshakeRequest::cleanupTestCase()
+{}
+
+void tst_HandshakeRequest::init()
+{
+ qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode");
+ qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
+}
+
+void tst_HandshakeRequest::cleanup()
+{
+}
+
+void tst_HandshakeRequest::tst_initialization()
+{
+ {
+ QWebSocketHandshakeRequest request(0, false);
+ QCOMPARE(request.port(), 0);
+ QVERIFY(!request.isSecure());
+ QVERIFY(!request.isValid());
+ QCOMPARE(request.extensions().length(), 0);
+ QCOMPARE(request.protocols().length(), 0);
+ QCOMPARE(request.headers().size(), 0);
+ QCOMPARE(request.key().length(), 0);
+ QCOMPARE(request.origin().length(), 0);
+ QCOMPARE(request.host().length(), 0);
+ QVERIFY(request.requestUrl().isEmpty());
+ QCOMPARE(request.resourceName().length(), 0);
+ QCOMPARE(request.versions().length(), 0);
+ }
+ {
+ QWebSocketHandshakeRequest request(80, true);
+ QCOMPARE(request.port(), 80);
+ QVERIFY(request.isSecure());
+ QVERIFY(!request.isValid());
+ QCOMPARE(request.extensions().length(), 0);
+ QCOMPARE(request.protocols().length(), 0);
+ QCOMPARE(request.headers().size(), 0);
+ QCOMPARE(request.key().length(), 0);
+ QCOMPARE(request.origin().length(), 0);
+ QCOMPARE(request.host().length(), 0);
+ QVERIFY(request.requestUrl().isEmpty());
+ QCOMPARE(request.resourceName().length(), 0);
+ QCOMPARE(request.versions().length(), 0);
+ }
+ {
+ QWebSocketHandshakeRequest request(80, true);
+ request.clear();
+ QCOMPARE(request.port(), 80);
+ QVERIFY(request.isSecure());
+ QVERIFY(!request.isValid());
+ QCOMPARE(request.extensions().length(), 0);
+ QCOMPARE(request.protocols().length(), 0);
+ QCOMPARE(request.headers().size(), 0);
+ QCOMPARE(request.key().length(), 0);
+ QCOMPARE(request.origin().length(), 0);
+ QCOMPARE(request.host().length(), 0);
+ QVERIFY(request.requestUrl().isEmpty());
+ QCOMPARE(request.resourceName().length(), 0);
+ QCOMPARE(request.versions().length(), 0);
+ }
+}
+
+void tst_HandshakeRequest::tst_invalidStream_data()
+{
+ QTest::addColumn<QString>("dataStream");
+
+ QTest::newRow("garbage on 2 lines") << QStringLiteral("foofoofoo\r\nfoofoo\r\n\r\n");
+ QTest::newRow("garbage on 1 line") << QStringLiteral("foofoofoofoofoo");
+ QTest::newRow("Correctly formatted but invalid fields")
+ << QStringLiteral("VERB RESOURCE PROTOCOL");
+
+ //internally the fields are parsed and indexes are used to convert
+ //to a http version for instance
+ //this test checks if there doesn't occur an out-of-bounds exception
+ QTest::newRow("Correctly formatted but invalid short fields") << QStringLiteral("V R P");
+ QTest::newRow("Invalid \\0 character in header") << QStringLiteral("V R\0 P");
+ QTest::newRow("Invalid HTTP version in header") << QStringLiteral("V R HTTP/invalid");
+ QTest::newRow("Empty header field") << QStringLiteral("GET . HTTP/1.1\r\nHEADER: ");
+ QTest::newRow("All zeros") << QString::fromUtf8(QByteArray(10, char(0)));
+ QTest::newRow("Invalid hostname") << QStringLiteral("GET . HTTP/1.1\r\nHost: \xFF\xFF");
+ //doing extensive QStringLiteral concatenations here, because
+ //MSVC 2010 complains when using concatenation literal strings about
+ //concatenation of wide and narrow strings (error C2308)
+ QTest::newRow("Complete header - Invalid WebSocket version")
+ << QStringLiteral("GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: ") +
+ QStringLiteral("\xFF\xFF\r\n") +
+ QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
+ QStringLiteral("Upgrade: websocket\r\n") +
+ QStringLiteral("Connection: Upgrade\r\n\r\n");
+ QTest::newRow("Complete header - Invalid verb")
+ << QStringLiteral("XXX . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n") +
+ QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
+ QStringLiteral("Upgrade: websocket\r\n") +
+ QStringLiteral("Connection: Upgrade\r\n\r\n");
+ QTest::newRow("Complete header - Invalid HTTP version")
+ << QStringLiteral("GET . HTTP/a.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n") +
+ QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
+ QStringLiteral("Upgrade: websocket\r\n") +
+ QStringLiteral("Connection: Upgrade\r\n\r\n");
+ QTest::newRow("Complete header - Invalid connection")
+ << QStringLiteral("GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n") +
+ QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
+ QStringLiteral("Upgrade: websocket\r\n") +
+ QStringLiteral("Connection: xxxxxxx\r\n\r\n");
+ QTest::newRow("Complete header - Invalid upgrade")
+ << QStringLiteral("GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n") +
+ QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
+ QStringLiteral("Upgrade: wabsocket\r\n") +
+ QStringLiteral("Connection: Upgrade\r\n\r\n");
+ QTest::newRow("Complete header - Upgrade contains too many values")
+ << QStringLiteral("GET . HTTP/1.1\r\nHost: foo\r\nSec-WebSocket-Version: 13\r\n") +
+ QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
+ QStringLiteral("Upgrade: websocket,ftp\r\n") +
+ QStringLiteral("Connection: Upgrade\r\n\r\n");
+}
+
+void tst_HandshakeRequest::tst_invalidStream()
+{
+ QFETCH(QString, dataStream);
+
+ QByteArray data;
+ QTextStream textStream(&data);
+ QWebSocketHandshakeRequest request(80, true);
+
+ textStream << dataStream;
+ textStream.seek(0);
+ request.readHandshake(textStream);
+
+ QVERIFY(!request.isValid());
+ QCOMPARE(request.port(), 80);
+ QVERIFY(request.isSecure());
+ QCOMPARE(request.extensions().length(), 0);
+ QCOMPARE(request.protocols().length(), 0);
+ QCOMPARE(request.headers().size(), 0);
+ QCOMPARE(request.key().length(), 0);
+ QCOMPARE(request.origin().length(), 0);
+ QCOMPARE(request.host().length(), 0);
+ QVERIFY(request.requestUrl().isEmpty());
+ QCOMPARE(request.resourceName().length(), 0);
+ QCOMPARE(request.versions().length(), 0);
+}
+
+/*
+ * This is a regression test
+ * Checks for validity when more than one value is present in Connection
+ */
+void tst_HandshakeRequest::tst_multipleValuesInConnectionHeader()
+{
+ //doing extensive QStringLiteral concatenations here, because
+ //MSVC 2010 complains when using concatenation literal strings about
+ //concatenation of wide and narrow strings (error C2308)
+ QString header = QStringLiteral("GET /test HTTP/1.1\r\nHost: ") +
+ QStringLiteral("foo.com\r\nSec-WebSocket-Version: 13\r\n") +
+ QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
+ QStringLiteral("Upgrade: websocket\r\n") +
+ QStringLiteral("Connection: Upgrade,keepalive\r\n\r\n");
+ QByteArray data;
+ QTextStream textStream(&data);
+ QWebSocketHandshakeRequest request(80, false);
+
+ textStream << header;
+ textStream.seek(0);
+ request.readHandshake(textStream);
+
+ QVERIFY(request.isValid());
+ QCOMPARE(request.port(), 80);
+ QVERIFY(!request.isSecure());
+ QCOMPARE(request.extensions().length(), 0);
+ QCOMPARE(request.protocols().length(), 0);
+ QCOMPARE(request.headers().size(), 5);
+ QCOMPARE(request.key().length(), 9);
+ QCOMPARE(request.origin().length(), 0);
+ QCOMPARE(request.requestUrl(), QUrl("ws://foo.com/test"));
+ QCOMPARE(request.host(), QStringLiteral("foo.com"));
+ QCOMPARE(request.resourceName().length(), 5);
+ QCOMPARE(request.versions().length(), 1);
+ QCOMPARE(request.versions().at(0), QWebSocketProtocol::Version13);
+}
+
+void tst_HandshakeRequest::tst_multipleVersions()
+{
+ QString header = QStringLiteral("GET /test HTTP/1.1\r\nHost: foo.com\r\n") +
+ QStringLiteral("Sec-WebSocket-Version: 4, 5, 6, 7, 8, 13\r\n") +
+ QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
+ QStringLiteral("Upgrade: websocket\r\n") +
+ QStringLiteral("Connection: Upgrade,keepalive\r\n\r\n");
+ QByteArray data;
+ QTextStream textStream(&data);
+ QWebSocketHandshakeRequest request(80, false);
+
+ textStream << header;
+ textStream.seek(0);
+ request.readHandshake(textStream);
+
+ QVERIFY(request.isValid());
+ QCOMPARE(request.port(), 80);
+ QVERIFY(!request.isSecure());
+ QCOMPARE(request.extensions().length(), 0);
+ QCOMPARE(request.protocols().length(), 0);
+ QCOMPARE(request.headers().size(), 5);
+ QVERIFY(request.headers().contains(QStringLiteral("host")));
+ QVERIFY(request.headers().contains(QStringLiteral("sec-websocket-version")));
+ QVERIFY(request.headers().contains(QStringLiteral("sec-websocket-key")));
+ QVERIFY(request.headers().contains(QStringLiteral("upgrade")));
+ QVERIFY(request.headers().contains(QStringLiteral("connection")));
+ QCOMPARE(request.key(), QStringLiteral("AVDFBDDFF"));
+ QCOMPARE(request.origin().length(), 0);
+ QCOMPARE(request.requestUrl(), QUrl("ws://foo.com/test"));
+ QCOMPARE(request.host(), QStringLiteral("foo.com"));
+ QCOMPARE(request.resourceName().length(), 5);
+ QCOMPARE(request.versions().length(), 6);
+ //should be 13 since the list is ordered in decreasing order
+ QCOMPARE(request.versions().at(0), QWebSocketProtocol::Version13);
+}
+
+void tst_HandshakeRequest::tst_qtbug_39355()
+{
+ QString header = QStringLiteral("GET /ABC/DEF/ HTTP/1.1\r\nHost: localhost:1234\r\n") +
+ QStringLiteral("Sec-WebSocket-Version: 13\r\n") +
+ QStringLiteral("Sec-WebSocket-Key: 2Wg20829/4ziWlmsUAD8Dg==\r\n") +
+ QStringLiteral("Upgrade: websocket\r\n") +
+ QStringLiteral("Connection: Upgrade\r\n\r\n");
+ QByteArray data;
+ QTextStream textStream(&data);
+ QWebSocketHandshakeRequest request(8080, false);
+
+ textStream << header;
+ textStream.seek(0);
+ request.readHandshake(textStream);
+
+ QVERIFY(request.isValid());
+ QCOMPARE(request.port(), 1234);
+ QCOMPARE(request.host(), QStringLiteral("localhost"));
+}
+
+QTEST_MAIN(tst_HandshakeRequest)
+
+#include "tst_handshakerequest.moc"
diff --git a/tests/auto/websockets/handshakeresponse/handshakeresponse.pro b/tests/auto/websockets/handshakeresponse/handshakeresponse.pro
new file mode 100644
index 0000000..4d7cd50
--- /dev/null
+++ b/tests/auto/websockets/handshakeresponse/handshakeresponse.pro
@@ -0,0 +1,14 @@
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+TARGET = tst_handshakeresponse
+
+QT = core testlib websockets websockets-private
+
+SOURCES += tst_handshakeresponse.cpp
+
+requires(contains(QT_CONFIG, private_tests))
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/websockets/handshakeresponse/tst_handshakeresponse.cpp b/tests/auto/websockets/handshakeresponse/tst_handshakeresponse.cpp
new file mode 100644
index 0000000..b5f103b
--- /dev/null
+++ b/tests/auto/websockets/handshakeresponse/tst_handshakeresponse.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QtTest/qtestcase.h>
+#include <QtCore/QDebug>
+#include <QtCore/QByteArray>
+#include <QtCore/QtEndian>
+
+#include "private/qwebsockethandshakerequest_p.h"
+#include "private/qwebsockethandshakeresponse_p.h"
+#include "private/qwebsocketprotocol_p.h"
+#include "QtWebSockets/qwebsocketprotocol.h"
+
+QT_USE_NAMESPACE
+
+Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
+Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
+
+class tst_HandshakeResponse : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_HandshakeResponse();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void tst_date_response();
+};
+
+tst_HandshakeResponse::tst_HandshakeResponse()
+{}
+
+void tst_HandshakeResponse::initTestCase()
+{
+}
+
+void tst_HandshakeResponse::cleanupTestCase()
+{}
+
+void tst_HandshakeResponse::init()
+{
+ qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode");
+ qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
+}
+
+void tst_HandshakeResponse::cleanup()
+{
+}
+
+void tst_HandshakeResponse::tst_date_response()
+{
+ QWebSocketHandshakeRequest request(80, false);
+ QString buffer;
+ QTextStream input(&buffer);
+ input << QStringLiteral("GET / HTTP/1.1\r\nHost: example.com\r\nSec-WebSocket-Version: 13\r\n") +
+ QStringLiteral("Sec-WebSocket-Key: AVDFBDDFF\r\n") +
+ QStringLiteral("Upgrade: websocket\r\n") +
+ QStringLiteral("Connection: Upgrade\r\n\r\n");
+ request.readHandshake(input);
+
+ QWebSocketHandshakeResponse response(request, "example.com", true,
+ QList<QWebSocketProtocol::Version>() << QWebSocketProtocol::Version13,
+ QList<QString>(),
+ QList<QString>());
+ QString data;
+ QTextStream output(&data);
+ output << response;
+
+ QStringList list = data.split("\r\n");
+ int index = list.indexOf(QRegExp("Date:.*"));
+ QVERIFY(index > -1);
+ QVERIFY(QLocale::c().toDateTime(list[index], "'Date:' ddd, dd MMM yyyy hh:mm:ss 'GMT'").isValid());
+}
+
+QTEST_MAIN(tst_HandshakeResponse)
+
+#include "tst_handshakeresponse.moc"
diff --git a/tests/auto/websockets/qdefaultmaskgenerator/qdefaultmaskgenerator.pro b/tests/auto/websockets/qdefaultmaskgenerator/qdefaultmaskgenerator.pro
new file mode 100644
index 0000000..3a951a7
--- /dev/null
+++ b/tests/auto/websockets/qdefaultmaskgenerator/qdefaultmaskgenerator.pro
@@ -0,0 +1,14 @@
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+TARGET = tst_defaultmaskgenerator
+
+QT = core testlib websockets websockets-private
+
+SOURCES += tst_defaultmaskgenerator.cpp
+
+requires(contains(QT_CONFIG, private_tests))
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/websockets/qdefaultmaskgenerator/tst_defaultmaskgenerator.cpp b/tests/auto/websockets/qdefaultmaskgenerator/tst_defaultmaskgenerator.cpp
new file mode 100644
index 0000000..f5fc5c4
--- /dev/null
+++ b/tests/auto/websockets/qdefaultmaskgenerator/tst_defaultmaskgenerator.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QtTest/qtestcase.h>
+#include <QtTest/QSignalSpy>
+#include <QtCore/QBuffer>
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+
+#include "private/qdefaultmaskgenerator_p.h"
+
+QT_USE_NAMESPACE
+
+class tst_QDefaultMaskGenerator : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QDefaultMaskGenerator();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void tst_randomnessWithoutSeed();
+ void tst_randomnessWithSeed();
+
+private:
+};
+
+
+
+tst_QDefaultMaskGenerator::tst_QDefaultMaskGenerator()
+{
+}
+
+void tst_QDefaultMaskGenerator::initTestCase()
+{
+}
+
+void tst_QDefaultMaskGenerator::cleanupTestCase()
+{
+}
+
+void tst_QDefaultMaskGenerator::init()
+{
+}
+
+void tst_QDefaultMaskGenerator::cleanup()
+{
+}
+
+void tst_QDefaultMaskGenerator::tst_randomnessWithoutSeed()
+{
+ //generate two series of data, and see if they differ
+ {
+ QDefaultMaskGenerator generator;
+
+ QVector<quint32> series1, series2;
+ for (int i = 0; i < 1000; ++i)
+ series1 << generator.nextMask();
+ for (int i = 0; i < 1000; ++i)
+ series2 << generator.nextMask();
+
+ QVERIFY(series1 != series2);
+ }
+}
+
+void tst_QDefaultMaskGenerator::tst_randomnessWithSeed()
+{
+ //generate two series of data, and see if they differ
+ //the generator is seeded
+ {
+ QDefaultMaskGenerator generator;
+ generator.seed();
+
+ QVector<quint32> series1, series2;
+ for (int i = 0; i < 1000; ++i)
+ series1 << generator.nextMask();
+ for (int i = 0; i < 1000; ++i)
+ series2 << generator.nextMask();
+
+ QVERIFY(series1 != series2);
+ }
+ //generates two series of data with 2 distinct generators
+ //both generators are seeded
+ {
+ QDefaultMaskGenerator generator1, generator2;
+ generator1.seed();
+ generator2.seed();
+
+ QVector<quint32> series1, series2;
+ for (int i = 0; i < 1000; ++i) {
+ series1 << generator1.nextMask();
+ series2 << generator2.nextMask();
+ }
+
+ QVERIFY(series1 != series2);
+ }
+ //generates two series of data with 2 distinct generators
+ //only one of the two is seeded
+ {
+ QDefaultMaskGenerator generator1, generator2;
+ generator1.seed();
+
+ QVector<quint32> series1, series2;
+ for (int i = 0; i < 1000; ++i) {
+ series1 << generator1.nextMask();
+ series2 << generator2.nextMask();
+ }
+
+ QVERIFY(series1 != series2);
+ }
+}
+
+QTEST_MAIN(tst_QDefaultMaskGenerator)
+
+#include "tst_defaultmaskgenerator.moc"
diff --git a/tests/auto/websockets/qwebsocket/qwebsocket.pro b/tests/auto/websockets/qwebsocket/qwebsocket.pro
new file mode 100644
index 0000000..0155d08
--- /dev/null
+++ b/tests/auto/websockets/qwebsocket/qwebsocket.pro
@@ -0,0 +1,13 @@
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+QT = core testlib websockets
+
+TARGET = tst_qwebsocket
+
+TEMPLATE = app
+
+SOURCES += tst_qwebsocket.cpp
+
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp b/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp
new file mode 100644
index 0000000..aca25d0
--- /dev/null
+++ b/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp
@@ -0,0 +1,605 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QString>
+#include <QtTest>
+#include <QtWebSockets/QWebSocket>
+#include <QtWebSockets/QWebSocketServer>
+#include <QtWebSockets/qwebsocketprotocol.h>
+
+QT_USE_NAMESPACE
+
+Q_DECLARE_METATYPE(QWebSocketProtocol::Version)
+
+class EchoServer : public QObject
+{
+ Q_OBJECT
+public:
+ explicit EchoServer(QObject *parent = Q_NULLPTR);
+ ~EchoServer();
+
+ QHostAddress hostAddress() const { return m_pWebSocketServer->serverAddress(); }
+ quint16 port() const { return m_pWebSocketServer->serverPort(); }
+
+Q_SIGNALS:
+ void newConnection(QUrl requestUrl);
+
+private Q_SLOTS:
+ void onNewConnection();
+ void processTextMessage(QString message);
+ void processBinaryMessage(QByteArray message);
+ void socketDisconnected();
+
+private:
+ QWebSocketServer *m_pWebSocketServer;
+ QList<QWebSocket *> m_clients;
+};
+
+EchoServer::EchoServer(QObject *parent) :
+ QObject(parent),
+ m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Echo Server"),
+ QWebSocketServer::NonSecureMode, this)),
+ m_clients()
+{
+ if (m_pWebSocketServer->listen(QHostAddress(QStringLiteral("127.0.0.1")))) {
+ connect(m_pWebSocketServer, SIGNAL(newConnection()),
+ this, SLOT(onNewConnection()));
+ }
+}
+
+EchoServer::~EchoServer()
+{
+ m_pWebSocketServer->close();
+ qDeleteAll(m_clients.begin(), m_clients.end());
+}
+
+void EchoServer::onNewConnection()
+{
+ QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
+
+ Q_EMIT newConnection(pSocket->requestUrl());
+
+ connect(pSocket, SIGNAL(textMessageReceived(QString)), this, SLOT(processTextMessage(QString)));
+ connect(pSocket, SIGNAL(binaryMessageReceived(QByteArray)), this, SLOT(processBinaryMessage(QByteArray)));
+ connect(pSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
+
+ m_clients << pSocket;
+}
+
+void EchoServer::processTextMessage(QString message)
+{
+ QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
+ if (pClient) {
+ pClient->sendTextMessage(message);
+ }
+}
+
+void EchoServer::processBinaryMessage(QByteArray message)
+{
+ QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
+ if (pClient) {
+ pClient->sendBinaryMessage(message);
+ }
+}
+
+void EchoServer::socketDisconnected()
+{
+ QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
+ if (pClient) {
+ m_clients.removeAll(pClient);
+ pClient->deleteLater();
+ }
+}
+
+class tst_QWebSocket : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QWebSocket();
+
+private Q_SLOTS:
+ void init();
+ void initTestCase();
+ void cleanupTestCase();
+ void tst_initialisation_data();
+ void tst_initialisation();
+ void tst_settersAndGetters();
+ void tst_invalidOpen_data();
+ void tst_invalidOpen();
+ void tst_invalidOrigin();
+ void tst_sendTextMessage();
+ void tst_sendBinaryMessage();
+ void tst_errorString();
+#ifndef QT_NO_NETWORKPROXY
+ void tst_setProxy();
+#endif
+};
+
+tst_QWebSocket::tst_QWebSocket()
+{
+}
+
+void tst_QWebSocket::init()
+{
+ qRegisterMetaType<QWebSocketProtocol::Version>("QWebSocketProtocol::Version");
+}
+
+void tst_QWebSocket::initTestCase()
+{
+}
+
+void tst_QWebSocket::cleanupTestCase()
+{
+}
+
+void tst_QWebSocket::tst_initialisation_data()
+{
+ QTest::addColumn<QString>("origin");
+ QTest::addColumn<QString>("expectedOrigin");
+ QTest::addColumn<QWebSocketProtocol::Version>("version");
+ QTest::addColumn<QWebSocketProtocol::Version>("expectedVersion");
+
+ QTest::newRow("Default origin and version")
+ << QString() << QString()
+ << QWebSocketProtocol::VersionUnknown << QWebSocketProtocol::VersionLatest;
+ QTest::newRow("Specific origin and default version")
+ << QStringLiteral("qt-project.org") << QStringLiteral("qt-project.org")
+ << QWebSocketProtocol::VersionUnknown << QWebSocketProtocol::VersionLatest;
+ QTest::newRow("Specific origin and specific version")
+ << QStringLiteral("qt-project.org") << QStringLiteral("qt-project.org")
+ << QWebSocketProtocol::Version7 << QWebSocketProtocol::Version7;
+}
+
+void tst_QWebSocket::tst_initialisation()
+{
+ QFETCH(QString, origin);
+ QFETCH(QString, expectedOrigin);
+ QFETCH(QWebSocketProtocol::Version, version);
+ QFETCH(QWebSocketProtocol::Version, expectedVersion);
+
+ QScopedPointer<QWebSocket> socket;
+
+ if (origin.isEmpty() && (version == QWebSocketProtocol::VersionUnknown))
+ socket.reset(new QWebSocket);
+ else if (!origin.isEmpty() && (version == QWebSocketProtocol::VersionUnknown))
+ socket.reset(new QWebSocket(origin));
+ else
+ socket.reset(new QWebSocket(origin, version));
+
+ QCOMPARE(socket->origin(), expectedOrigin);
+ QCOMPARE(socket->version(), expectedVersion);
+ QCOMPARE(socket->error(), QAbstractSocket::UnknownSocketError);
+ QVERIFY(socket->errorString().isEmpty());
+ QVERIFY(!socket->isValid());
+ QVERIFY(socket->localAddress().isNull());
+ QCOMPARE(socket->localPort(), quint16(0));
+ QCOMPARE(socket->pauseMode(), QAbstractSocket::PauseNever);
+ QVERIFY(socket->peerAddress().isNull());
+ QCOMPARE(socket->peerPort(), quint16(0));
+ QVERIFY(socket->peerName().isEmpty());
+ QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState);
+ QCOMPARE(socket->readBufferSize(), 0);
+ QVERIFY(socket->resourceName().isEmpty());
+ QVERIFY(!socket->requestUrl().isValid());
+ QCOMPARE(socket->closeCode(), QWebSocketProtocol::CloseCodeNormal);
+ QVERIFY(socket->closeReason().isEmpty());
+ QVERIFY(socket->flush());
+ QCOMPARE(socket->sendTextMessage(QStringLiteral("A text message")), 0);
+ QCOMPARE(socket->sendBinaryMessage(QByteArrayLiteral("A binary message")), 0);
+}
+
+void tst_QWebSocket::tst_settersAndGetters()
+{
+ QWebSocket socket;
+
+ socket.setPauseMode(QAbstractSocket::PauseNever);
+ QCOMPARE(socket.pauseMode(), QAbstractSocket::PauseNever);
+ socket.setPauseMode(QAbstractSocket::PauseOnSslErrors);
+ QCOMPARE(socket.pauseMode(), QAbstractSocket::PauseOnSslErrors);
+
+ socket.setReadBufferSize(0);
+ QCOMPARE(socket.readBufferSize(), 0);
+ socket.setReadBufferSize(128);
+ QCOMPARE(socket.readBufferSize(), 128);
+ socket.setReadBufferSize(-1);
+ QCOMPARE(socket.readBufferSize(), -1);
+}
+
+void tst_QWebSocket::tst_invalidOpen_data()
+{
+ QTest::addColumn<QString>("url");
+ QTest::addColumn<QString>("expectedUrl");
+ QTest::addColumn<QString>("expectedPeerName");
+ QTest::addColumn<QString>("expectedResourceName");
+ QTest::addColumn<QAbstractSocket::SocketState>("stateAfterOpenCall");
+ QTest::addColumn<int>("disconnectedCount");
+ QTest::addColumn<int>("stateChangedCount");
+
+ QTest::newRow("Illegal local address")
+ << QStringLiteral("ws://127.0.0.1:1/") << QStringLiteral("ws://127.0.0.1:1/")
+ << QStringLiteral("127.0.0.1")
+ << QStringLiteral("/") << QAbstractSocket::ConnectingState
+ << 1
+ << 2; //going from connecting to disconnected
+ QTest::newRow("URL containing new line in the hostname")
+ << QStringLiteral("ws://myhacky\r\nserver/") << QString()
+ << QString()
+ << QString() << QAbstractSocket::UnconnectedState
+ << 0 << 0;
+ QTest::newRow("URL containing new line in the resource name")
+ << QStringLiteral("ws://127.0.0.1:1/tricky\r\npath") << QString()
+ << QString()
+ << QString()
+ << QAbstractSocket::UnconnectedState
+ << 0 << 0;
+}
+
+void tst_QWebSocket::tst_invalidOpen()
+{
+ QFETCH(QString, url);
+ QFETCH(QString, expectedUrl);
+ QFETCH(QString, expectedPeerName);
+ QFETCH(QString, expectedResourceName);
+ QFETCH(QAbstractSocket::SocketState, stateAfterOpenCall);
+ QFETCH(int, disconnectedCount);
+ QFETCH(int, stateChangedCount);
+ QWebSocket socket;
+ QSignalSpy errorSpy(&socket, SIGNAL(error(QAbstractSocket::SocketError)));
+ QSignalSpy aboutToCloseSpy(&socket, SIGNAL(aboutToClose()));
+ QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
+ QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
+ QSignalSpy stateChangedSpy(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)));
+ QSignalSpy readChannelFinishedSpy(&socket, SIGNAL(readChannelFinished()));
+ QSignalSpy textFrameReceivedSpy(&socket, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy binaryFrameReceivedSpy(&socket, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+ QSignalSpy textMessageReceivedSpy(&socket, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryMessageReceivedSpy(&socket, SIGNAL(binaryMessageReceived(QByteArray)));
+ QSignalSpy pongSpy(&socket, SIGNAL(pong(quint64,QByteArray)));
+ QSignalSpy bytesWrittenSpy(&socket, SIGNAL(bytesWritten(qint64)));
+
+ socket.open(QUrl(url));
+
+ QVERIFY(socket.origin().isEmpty());
+ QCOMPARE(socket.version(), QWebSocketProtocol::VersionLatest);
+ //at this point the socket is in a connecting state
+ //so, there should no error at this point
+ QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError);
+ QVERIFY(!socket.errorString().isEmpty());
+ QVERIFY(!socket.isValid());
+ QVERIFY(socket.localAddress().isNull());
+ QCOMPARE(socket.localPort(), quint16(0));
+ QCOMPARE(socket.pauseMode(), QAbstractSocket::PauseNever);
+ QVERIFY(socket.peerAddress().isNull());
+ QCOMPARE(socket.peerPort(), quint16(0));
+ QCOMPARE(socket.peerName(), expectedPeerName);
+ QCOMPARE(socket.state(), stateAfterOpenCall);
+ QCOMPARE(socket.readBufferSize(), 0);
+ QCOMPARE(socket.resourceName(), expectedResourceName);
+ QCOMPARE(socket.requestUrl().toString(), expectedUrl);
+ QCOMPARE(socket.closeCode(), QWebSocketProtocol::CloseCodeNormal);
+ QVERIFY(socket.closeReason().isEmpty());
+ QCOMPARE(socket.sendTextMessage(QStringLiteral("A text message")), 0);
+ QCOMPARE(socket.sendBinaryMessage(QByteArrayLiteral("A text message")), 0);
+
+ if (errorSpy.count() == 0)
+ QVERIFY(errorSpy.wait());
+ QCOMPARE(errorSpy.count(), 1);
+ QList<QVariant> arguments = errorSpy.takeFirst();
+ QAbstractSocket::SocketError socketError =
+ qvariant_cast<QAbstractSocket::SocketError>(arguments.at(0));
+ QCOMPARE(socketError, QAbstractSocket::ConnectionRefusedError);
+ QCOMPARE(aboutToCloseSpy.count(), 0);
+ QCOMPARE(connectedSpy.count(), 0);
+ QCOMPARE(disconnectedSpy.count(), disconnectedCount);
+ QCOMPARE(stateChangedSpy.count(), stateChangedCount);
+ if (stateChangedCount == 2) {
+ arguments = stateChangedSpy.takeFirst();
+ QAbstractSocket::SocketState socketState =
+ qvariant_cast<QAbstractSocket::SocketState>(arguments.at(0));
+ arguments = stateChangedSpy.takeFirst();
+ socketState = qvariant_cast<QAbstractSocket::SocketState>(arguments.at(0));
+ QCOMPARE(socketState, QAbstractSocket::UnconnectedState);
+ }
+ QCOMPARE(readChannelFinishedSpy.count(), 0);
+ QCOMPARE(textFrameReceivedSpy.count(), 0);
+ QCOMPARE(binaryFrameReceivedSpy.count(), 0);
+ QCOMPARE(textMessageReceivedSpy.count(), 0);
+ QCOMPARE(binaryMessageReceivedSpy.count(), 0);
+ QCOMPARE(pongSpy.count(), 0);
+ QCOMPARE(bytesWrittenSpy.count(), 0);
+}
+
+void tst_QWebSocket::tst_invalidOrigin()
+{
+ QWebSocket socket(QStringLiteral("My server\r\nin the wild."));
+
+ QSignalSpy errorSpy(&socket, SIGNAL(error(QAbstractSocket::SocketError)));
+ QSignalSpy aboutToCloseSpy(&socket, SIGNAL(aboutToClose()));
+ QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
+ QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
+ QSignalSpy stateChangedSpy(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)));
+ QSignalSpy readChannelFinishedSpy(&socket, SIGNAL(readChannelFinished()));
+ QSignalSpy textFrameReceivedSpy(&socket, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy binaryFrameReceivedSpy(&socket, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+ QSignalSpy textMessageReceivedSpy(&socket, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy binaryMessageReceivedSpy(&socket, SIGNAL(binaryMessageReceived(QByteArray)));
+ QSignalSpy pongSpy(&socket, SIGNAL(pong(quint64,QByteArray)));
+ QSignalSpy bytesWrittenSpy(&socket, SIGNAL(bytesWritten(qint64)));
+
+ socket.open(QUrl(QStringLiteral("ws://127.0.0.1:1/")));
+
+ //at this point the socket is in a connecting state
+ //so, there should no error at this point
+ QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError);
+ QVERIFY(!socket.errorString().isEmpty());
+ QVERIFY(!socket.isValid());
+ QVERIFY(socket.localAddress().isNull());
+ QCOMPARE(socket.localPort(), quint16(0));
+ QCOMPARE(socket.pauseMode(), QAbstractSocket::PauseNever);
+ QVERIFY(socket.peerAddress().isNull());
+ QCOMPARE(socket.peerPort(), quint16(0));
+ QCOMPARE(socket.peerName(), QStringLiteral("127.0.0.1"));
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectingState);
+ QCOMPARE(socket.readBufferSize(), 0);
+ QCOMPARE(socket.resourceName(), QStringLiteral("/"));
+ QCOMPARE(socket.requestUrl(), QUrl(QStringLiteral("ws://127.0.0.1:1/")));
+ QCOMPARE(socket.closeCode(), QWebSocketProtocol::CloseCodeNormal);
+
+ QVERIFY(errorSpy.wait());
+
+ QCOMPARE(errorSpy.count(), 1);
+ QList<QVariant> arguments = errorSpy.takeFirst();
+ QAbstractSocket::SocketError socketError =
+ qvariant_cast<QAbstractSocket::SocketError>(arguments.at(0));
+ QCOMPARE(socketError, QAbstractSocket::ConnectionRefusedError);
+ QCOMPARE(aboutToCloseSpy.count(), 0);
+ QCOMPARE(connectedSpy.count(), 0);
+ QCOMPARE(disconnectedSpy.count(), 1);
+ QCOMPARE(stateChangedSpy.count(), 2); //connectingstate, unconnectedstate
+ arguments = stateChangedSpy.takeFirst();
+ QAbstractSocket::SocketState socketState =
+ qvariant_cast<QAbstractSocket::SocketState>(arguments.at(0));
+ arguments = stateChangedSpy.takeFirst();
+ socketState = qvariant_cast<QAbstractSocket::SocketState>(arguments.at(0));
+ QCOMPARE(socketState, QAbstractSocket::UnconnectedState);
+ QCOMPARE(readChannelFinishedSpy.count(), 0);
+ QCOMPARE(textFrameReceivedSpy.count(), 0);
+ QCOMPARE(binaryFrameReceivedSpy.count(), 0);
+ QCOMPARE(textMessageReceivedSpy.count(), 0);
+ QCOMPARE(binaryMessageReceivedSpy.count(), 0);
+ QCOMPARE(pongSpy.count(), 0);
+ QCOMPARE(bytesWrittenSpy.count(), 0);
+}
+
+void tst_QWebSocket::tst_sendTextMessage()
+{
+ EchoServer echoServer;
+
+ QWebSocket socket;
+
+ //should return 0 because socket is not open yet
+ QCOMPARE(socket.sendTextMessage(QStringLiteral("1234")), 0);
+
+ QSignalSpy socketConnectedSpy(&socket, SIGNAL(connected()));
+ QSignalSpy serverConnectedSpy(&echoServer, SIGNAL(newConnection(QUrl)));
+ QSignalSpy textMessageReceived(&socket, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy textFrameReceived(&socket, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy binaryMessageReceived(&socket, SIGNAL(binaryMessageReceived(QByteArray)));
+ QSignalSpy binaryFrameReceived(&socket, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+ QSignalSpy socketError(&socket, SIGNAL(error(QAbstractSocket::SocketError)));
+
+ QUrl url = QUrl(QStringLiteral("ws://") + echoServer.hostAddress().toString() +
+ QStringLiteral(":") + QString::number(echoServer.port()));
+ url.setPath("/segment/with spaces");
+ url.addQueryItem("queryitem", "with encoded characters");
+
+ socket.open(url);
+
+ if (socketConnectedSpy.count() == 0)
+ QVERIFY(socketConnectedSpy.wait(500));
+ QCOMPARE(socketError.count(), 0);
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(serverConnectedSpy.count(), 1);
+ QList<QVariant> arguments = serverConnectedSpy.takeFirst();
+ QUrl urlConnected = arguments.at(0).toUrl();
+ QCOMPARE(urlConnected, url);
+
+ socket.sendTextMessage(QStringLiteral("Hello world!"));
+
+ QVERIFY(textMessageReceived.wait(500));
+ QCOMPARE(textMessageReceived.count(), 1);
+ QCOMPARE(binaryMessageReceived.count(), 0);
+ QCOMPARE(binaryFrameReceived.count(), 0);
+ arguments = textMessageReceived.takeFirst();
+ QString messageReceived = arguments.at(0).toString();
+ QCOMPARE(messageReceived, QStringLiteral("Hello world!"));
+
+ QCOMPARE(textFrameReceived.count(), 1);
+ arguments = textFrameReceived.takeFirst();
+ QString frameReceived = arguments.at(0).toString();
+ bool isLastFrame = arguments.at(1).toBool();
+ QCOMPARE(frameReceived, QStringLiteral("Hello world!"));
+ QVERIFY(isLastFrame);
+
+ socket.close();
+
+ //QTBUG-36762: QWebSocket emits multiplied signals when socket was reopened
+ socketConnectedSpy.clear();
+ textMessageReceived.clear();
+ textFrameReceived.clear();
+
+ socket.open(QUrl(QStringLiteral("ws://") + echoServer.hostAddress().toString() +
+ QStringLiteral(":") + QString::number(echoServer.port())));
+
+ if (socketConnectedSpy.count() == 0)
+ QVERIFY(socketConnectedSpy.wait(500));
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+
+ socket.sendTextMessage(QStringLiteral("Hello world!"));
+
+ QVERIFY(textMessageReceived.wait(500));
+ QCOMPARE(textMessageReceived.count(), 1);
+ QCOMPARE(binaryMessageReceived.count(), 0);
+ QCOMPARE(binaryFrameReceived.count(), 0);
+ arguments = textMessageReceived.takeFirst();
+ messageReceived = arguments.at(0).toString();
+ QCOMPARE(messageReceived, QStringLiteral("Hello world!"));
+
+ QCOMPARE(textFrameReceived.count(), 1);
+ arguments = textFrameReceived.takeFirst();
+ frameReceived = arguments.at(0).toString();
+ isLastFrame = arguments.at(1).toBool();
+ QCOMPARE(frameReceived, QStringLiteral("Hello world!"));
+ QVERIFY(isLastFrame);
+
+ QString reason = QStringLiteral("going away");
+ socket.close(QWebSocketProtocol::CloseCodeGoingAway, reason);
+ QCOMPARE(socket.closeCode(), QWebSocketProtocol::CloseCodeGoingAway);
+ QCOMPARE(socket.closeReason(), reason);
+}
+
+void tst_QWebSocket::tst_sendBinaryMessage()
+{
+ EchoServer echoServer;
+
+ QWebSocket socket;
+
+ //should return 0 because socket is not open yet
+ QCOMPARE(socket.sendBinaryMessage(QByteArrayLiteral("1234")), 0);
+
+ QSignalSpy socketConnectedSpy(&socket, SIGNAL(connected()));
+ QSignalSpy textMessageReceived(&socket, SIGNAL(textMessageReceived(QString)));
+ QSignalSpy textFrameReceived(&socket, SIGNAL(textFrameReceived(QString,bool)));
+ QSignalSpy binaryMessageReceived(&socket, SIGNAL(binaryMessageReceived(QByteArray)));
+ QSignalSpy binaryFrameReceived(&socket, SIGNAL(binaryFrameReceived(QByteArray,bool)));
+
+ socket.open(QUrl(QStringLiteral("ws://") + echoServer.hostAddress().toString() +
+ QStringLiteral(":") + QString::number(echoServer.port())));
+
+ if (socketConnectedSpy.count() == 0)
+ QVERIFY(socketConnectedSpy.wait(500));
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+
+ socket.sendBinaryMessage(QByteArrayLiteral("Hello world!"));
+
+ QVERIFY(binaryMessageReceived.wait(500));
+ QCOMPARE(textMessageReceived.count(), 0);
+ QCOMPARE(textFrameReceived.count(), 0);
+ QCOMPARE(binaryMessageReceived.count(), 1);
+ QList<QVariant> arguments = binaryMessageReceived.takeFirst();
+ QByteArray messageReceived = arguments.at(0).toByteArray();
+ QCOMPARE(messageReceived, QByteArrayLiteral("Hello world!"));
+
+ QCOMPARE(binaryFrameReceived.count(), 1);
+ arguments = binaryFrameReceived.takeFirst();
+ QByteArray frameReceived = arguments.at(0).toByteArray();
+ bool isLastFrame = arguments.at(1).toBool();
+ QCOMPARE(frameReceived, QByteArrayLiteral("Hello world!"));
+ QVERIFY(isLastFrame);
+
+ socket.close();
+
+ //QTBUG-36762: QWebSocket emits multiple signals when socket is reopened
+ socketConnectedSpy.clear();
+ binaryMessageReceived.clear();
+ binaryFrameReceived.clear();
+
+ socket.open(QUrl(QStringLiteral("ws://") + echoServer.hostAddress().toString() +
+ QStringLiteral(":") + QString::number(echoServer.port())));
+
+ if (socketConnectedSpy.count() == 0)
+ QVERIFY(socketConnectedSpy.wait(500));
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+
+ socket.sendBinaryMessage(QByteArrayLiteral("Hello world!"));
+
+ QVERIFY(binaryMessageReceived.wait(500));
+ QCOMPARE(textMessageReceived.count(), 0);
+ QCOMPARE(textFrameReceived.count(), 0);
+ QCOMPARE(binaryMessageReceived.count(), 1);
+ arguments = binaryMessageReceived.takeFirst();
+ messageReceived = arguments.at(0).toByteArray();
+ QCOMPARE(messageReceived, QByteArrayLiteral("Hello world!"));
+
+ QCOMPARE(binaryFrameReceived.count(), 1);
+ arguments = binaryFrameReceived.takeFirst();
+ frameReceived = arguments.at(0).toByteArray();
+ isLastFrame = arguments.at(1).toBool();
+ QCOMPARE(frameReceived, QByteArrayLiteral("Hello world!"));
+ QVERIFY(isLastFrame);
+}
+
+void tst_QWebSocket::tst_errorString()
+{
+ //Check for QTBUG-37228: QWebSocket returns "Unknown Error" for known errors
+ QWebSocket socket;
+
+ //check that the default error string is empty
+ QVERIFY(socket.errorString().isEmpty());
+
+ QSignalSpy errorSpy(&socket, SIGNAL(error(QAbstractSocket::SocketError)));
+
+ socket.open(QUrl(QStringLiteral("ws://someserver.on.mars:9999")));
+
+ if (errorSpy.count() == 0)
+ errorSpy.wait(500);
+ QCOMPARE(errorSpy.count(), 1);
+ QList<QVariant> arguments = errorSpy.takeFirst();
+ QAbstractSocket::SocketError socketError =
+ qvariant_cast<QAbstractSocket::SocketError>(arguments.at(0));
+ QCOMPARE(socketError, QAbstractSocket::HostNotFoundError);
+ QCOMPARE(socket.errorString(), QStringLiteral("Host not found"));
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void tst_QWebSocket::tst_setProxy()
+{
+ // check if property assignment works as expected.
+ QWebSocket socket;
+ QCOMPARE(socket.proxy(), QNetworkProxy(QNetworkProxy::DefaultProxy));
+
+ QNetworkProxy proxy;
+ proxy.setPort(123);
+ socket.setProxy(proxy);
+ QCOMPARE(socket.proxy(), proxy);
+
+ proxy.setPort(321);
+ QCOMPARE(socket.proxy().port(), quint16(123));
+ socket.setProxy(proxy);
+ QCOMPARE(socket.proxy(), proxy);
+}
+#endif // QT_NO_NETWORKPROXY
+
+QTEST_MAIN(tst_QWebSocket)
+
+#include "tst_qwebsocket.moc"
diff --git a/tests/auto/websockets/qwebsocketcorsauthenticator/qwebsocketcorsauthenticator.pro b/tests/auto/websockets/qwebsocketcorsauthenticator/qwebsocketcorsauthenticator.pro
new file mode 100644
index 0000000..aa485fc
--- /dev/null
+++ b/tests/auto/websockets/qwebsocketcorsauthenticator/qwebsocketcorsauthenticator.pro
@@ -0,0 +1,13 @@
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+TARGET = tst_qwebsocketcorsauthenticator
+
+QT = core testlib websockets
+
+SOURCES += tst_qwebsocketcorsauthenticator.cpp
+
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/websockets/qwebsocketcorsauthenticator/tst_qwebsocketcorsauthenticator.cpp b/tests/auto/websockets/qwebsocketcorsauthenticator/tst_qwebsocketcorsauthenticator.cpp
new file mode 100644
index 0000000..225adc8
--- /dev/null
+++ b/tests/auto/websockets/qwebsocketcorsauthenticator/tst_qwebsocketcorsauthenticator.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QtTest/qtestcase.h>
+#include <QtCore/QDebug>
+#include <QtCore/QByteArray>
+#include <QtCore/QtEndian>
+
+#include "QtWebSockets/qwebsocketcorsauthenticator.h"
+
+QT_USE_NAMESPACE
+
+class tst_QWebSocketCorsAuthenticator : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QWebSocketCorsAuthenticator();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void tst_initialization();
+};
+
+tst_QWebSocketCorsAuthenticator::tst_QWebSocketCorsAuthenticator()
+{}
+
+void tst_QWebSocketCorsAuthenticator::initTestCase()
+{
+}
+
+void tst_QWebSocketCorsAuthenticator::cleanupTestCase()
+{}
+
+void tst_QWebSocketCorsAuthenticator::init()
+{
+}
+
+void tst_QWebSocketCorsAuthenticator::cleanup()
+{
+}
+
+void tst_QWebSocketCorsAuthenticator::tst_initialization()
+{
+ {
+ QWebSocketCorsAuthenticator authenticator((QString()));
+
+ QCOMPARE(authenticator.allowed(), true);
+ QCOMPARE(authenticator.origin(), QString());
+ }
+ {
+ QWebSocketCorsAuthenticator authenticator(QStringLiteral("com.somesite"));
+
+ QCOMPARE(authenticator.allowed(), true);
+ QCOMPARE(authenticator.origin(), QStringLiteral("com.somesite"));
+
+ QWebSocketCorsAuthenticator other(authenticator);
+ QCOMPARE(other.origin(), authenticator.origin());
+ QCOMPARE(other.allowed(), authenticator.allowed());
+
+ authenticator.setAllowed(false);
+ QVERIFY(!authenticator.allowed());
+ QCOMPARE(other.allowed(), true); //make sure other is a real copy
+
+ authenticator.setAllowed(true);
+ QVERIFY(authenticator.allowed());
+
+ authenticator.setAllowed(false);
+ other = authenticator;
+ QCOMPARE(other.origin(), authenticator.origin());
+ QCOMPARE(other.allowed(), authenticator.allowed());
+ }
+}
+
+QTEST_MAIN(tst_QWebSocketCorsAuthenticator)
+
+#include "tst_qwebsocketcorsauthenticator.moc"
+
diff --git a/tests/auto/websockets/qwebsocketserver/qwebsocketserver.pro b/tests/auto/websockets/qwebsocketserver/qwebsocketserver.pro
new file mode 100644
index 0000000..25216d3
--- /dev/null
+++ b/tests/auto/websockets/qwebsocketserver/qwebsocketserver.pro
@@ -0,0 +1,13 @@
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+QT = core testlib websockets
+
+TARGET = tst_qwebsocketserver
+
+TEMPLATE = app
+
+SOURCES += tst_qwebsocketserver.cpp
+
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp b/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
new file mode 100644
index 0000000..c2ba842
--- /dev/null
+++ b/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
@@ -0,0 +1,390 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QString>
+#include <QtTest>
+#include <QNetworkProxy>
+#include <QtWebSockets/QWebSocketServer>
+#include <QtWebSockets/QWebSocket>
+#include <QtWebSockets/QWebSocketCorsAuthenticator>
+#include <QtWebSockets/qwebsocketprotocol.h>
+
+QT_USE_NAMESPACE
+
+Q_DECLARE_METATYPE(QWebSocketProtocol::Version)
+Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
+Q_DECLARE_METATYPE(QWebSocketServer::SslMode)
+Q_DECLARE_METATYPE(QWebSocketCorsAuthenticator *)
+#ifndef QT_NO_SSL
+Q_DECLARE_METATYPE(QSslError)
+#endif
+
+class tst_QWebSocketServer : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QWebSocketServer();
+
+private Q_SLOTS:
+ void init();
+ void initTestCase();
+ void cleanupTestCase();
+ void tst_initialisation();
+ void tst_settersAndGetters();
+ void tst_listening();
+ void tst_connectivity();
+ void tst_maxPendingConnections();
+ void tst_serverDestroyedWhileSocketConnected();
+};
+
+tst_QWebSocketServer::tst_QWebSocketServer()
+{
+}
+
+void tst_QWebSocketServer::init()
+{
+ qRegisterMetaType<QWebSocketProtocol::Version>("QWebSocketProtocol::Version");
+ qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
+ qRegisterMetaType<QWebSocketServer::SslMode>("QWebSocketServer::SslMode");
+ qRegisterMetaType<QWebSocketCorsAuthenticator *>("QWebSocketCorsAuthenticator *");
+#ifndef QT_NO_SSL
+ qRegisterMetaType<QSslError>("QSslError");
+#endif
+}
+
+void tst_QWebSocketServer::initTestCase()
+{
+}
+
+void tst_QWebSocketServer::cleanupTestCase()
+{
+}
+
+void tst_QWebSocketServer::tst_initialisation()
+{
+ {
+ QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
+
+ QVERIFY(server.serverName().isEmpty());
+ QCOMPARE(server.secureMode(), QWebSocketServer::NonSecureMode);
+ QVERIFY(!server.isListening());
+ QCOMPARE(server.maxPendingConnections(), 30);
+ QCOMPARE(server.serverPort(), quint16(0));
+ QCOMPARE(server.serverAddress(), QHostAddress());
+ QCOMPARE(server.socketDescriptor(), -1);
+ QVERIFY(!server.hasPendingConnections());
+ QVERIFY(!server.nextPendingConnection());
+ QCOMPARE(server.error(), QWebSocketProtocol::CloseCodeNormal);
+ QVERIFY(server.errorString().isEmpty());
+ #ifndef QT_NO_NETWORKPROXY
+ QCOMPARE(server.proxy().type(), QNetworkProxy::DefaultProxy);
+ #endif
+ #ifndef QT_NO_SSL
+ QCOMPARE(server.sslConfiguration(), QSslConfiguration::defaultConfiguration());
+ #endif
+ QCOMPARE(server.supportedVersions().count(), 1);
+ QCOMPARE(server.supportedVersions().at(0), QWebSocketProtocol::VersionLatest);
+ QCOMPARE(server.supportedVersions().at(0), QWebSocketProtocol::Version13);
+
+ server.close();
+ //closing a server should not affect any of the parameters
+ //certainly if the server was not opened before
+
+ QVERIFY(server.serverName().isEmpty());
+ QCOMPARE(server.secureMode(), QWebSocketServer::NonSecureMode);
+ QVERIFY(!server.isListening());
+ QCOMPARE(server.maxPendingConnections(), 30);
+ QCOMPARE(server.serverPort(), quint16(0));
+ QCOMPARE(server.serverAddress(), QHostAddress());
+ QCOMPARE(server.socketDescriptor(), -1);
+ QVERIFY(!server.hasPendingConnections());
+ QVERIFY(!server.nextPendingConnection());
+ QCOMPARE(server.error(), QWebSocketProtocol::CloseCodeNormal);
+ QVERIFY(server.errorString().isEmpty());
+ #ifndef QT_NO_NETWORKPROXY
+ QCOMPARE(server.proxy().type(), QNetworkProxy::DefaultProxy);
+ #endif
+ #ifndef QT_NO_SSL
+ QCOMPARE(server.sslConfiguration(), QSslConfiguration::defaultConfiguration());
+ #endif
+ QCOMPARE(server.supportedVersions().count(), 1);
+ QCOMPARE(server.supportedVersions().at(0), QWebSocketProtocol::VersionLatest);
+ QCOMPARE(server.supportedVersions().at(0), QWebSocketProtocol::Version13);
+ QCOMPARE(server.serverUrl(), QUrl());
+ }
+
+ {
+#ifndef QT_NO_SSL
+ QWebSocketServer sslServer(QString(), QWebSocketServer::SecureMode);
+ QCOMPARE(sslServer.secureMode(), QWebSocketServer::SecureMode);
+#endif
+ }
+}
+
+void tst_QWebSocketServer::tst_settersAndGetters()
+{
+ QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
+
+ server.setMaxPendingConnections(23);
+ QCOMPARE(server.maxPendingConnections(), 23);
+ server.setMaxPendingConnections(INT_MIN);
+ QCOMPARE(server.maxPendingConnections(), INT_MIN);
+ server.setMaxPendingConnections(INT_MAX);
+ QCOMPARE(server.maxPendingConnections(), INT_MAX);
+
+ QVERIFY(!server.setSocketDescriptor(-2));
+ QCOMPARE(server.socketDescriptor(), -1);
+
+ server.setServerName(QStringLiteral("Qt WebSocketServer"));
+ QCOMPARE(server.serverName(), QStringLiteral("Qt WebSocketServer"));
+
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy proxy(QNetworkProxy::Socks5Proxy);
+ server.setProxy(proxy);
+ QCOMPARE(server.proxy(), proxy);
+#endif
+#ifndef QT_NO_SSL
+ //cannot set an ssl configuration on a non secure server
+ QSslConfiguration sslConfiguration = QSslConfiguration::defaultConfiguration();
+ sslConfiguration.setPeerVerifyDepth(sslConfiguration.peerVerifyDepth() + 1);
+ server.setSslConfiguration(sslConfiguration);
+ QVERIFY(server.sslConfiguration() != sslConfiguration);
+ QCOMPARE(server.sslConfiguration(), QSslConfiguration::defaultConfiguration());
+
+ QWebSocketServer sslServer(QString(), QWebSocketServer::SecureMode);
+ sslServer.setSslConfiguration(sslConfiguration);
+ QCOMPARE(sslServer.sslConfiguration(), sslConfiguration);
+ QVERIFY(sslServer.sslConfiguration() != QSslConfiguration::defaultConfiguration());
+#endif
+}
+
+void tst_QWebSocketServer::tst_listening()
+{
+ //These listening tests are not too extensive, as the implementation of QWebSocketServer
+ //relies on QTcpServer
+
+ QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
+
+ QSignalSpy serverAcceptErrorSpy(&server, SIGNAL(acceptError(QAbstractSocket::SocketError)));
+ QSignalSpy serverConnectionSpy(&server, SIGNAL(newConnection()));
+ QSignalSpy serverErrorSpy(&server,
+ SIGNAL(serverError(QWebSocketProtocol::CloseCode)));
+ QSignalSpy corsAuthenticationSpy(&server,
+ SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)));
+ QSignalSpy serverClosedSpy(&server, SIGNAL(closed()));
+#ifndef QT_NO_SSL
+ QSignalSpy peerVerifyErrorSpy(&server, SIGNAL(peerVerifyError(QSslError)));
+ QSignalSpy sslErrorsSpy(&server, SIGNAL(sslErrors(QList<QSslError>)));
+#endif
+
+ QVERIFY(server.listen()); //listen on all network interface, choose an appropriate port
+ QVERIFY(server.isListening());
+ QCOMPARE(serverClosedSpy.count(), 0);
+ server.close();
+ QVERIFY(serverClosedSpy.wait(1000));
+ QVERIFY(!server.isListening());
+ QCOMPARE(serverErrorSpy.count(), 0);
+
+ QVERIFY(!server.listen(QHostAddress(QStringLiteral("1.2.3.4")), 0));
+ QCOMPARE(server.error(), QWebSocketProtocol::CloseCodeAbnormalDisconnection);
+ QCOMPARE(server.errorString().toLatin1().constData(), "The address is not available");
+ QVERIFY(!server.isListening());
+
+ QCOMPARE(serverAcceptErrorSpy.count(), 0);
+ QCOMPARE(serverConnectionSpy.count(), 0);
+ QCOMPARE(corsAuthenticationSpy.count(), 0);
+#ifndef QT_NO_SSL
+ QCOMPARE(peerVerifyErrorSpy.count(), 0);
+ QCOMPARE(sslErrorsSpy.count(), 0);
+#endif
+ QCOMPARE(serverErrorSpy.count(), 1);
+ QCOMPARE(serverClosedSpy.count(), 1);
+}
+
+void tst_QWebSocketServer::tst_connectivity()
+{
+ QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
+ QSignalSpy serverConnectionSpy(&server, SIGNAL(newConnection()));
+ QSignalSpy serverErrorSpy(&server,
+ SIGNAL(serverError(QWebSocketProtocol::CloseCode)));
+ QSignalSpy corsAuthenticationSpy(&server,
+ SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)));
+ QSignalSpy serverClosedSpy(&server, SIGNAL(closed()));
+#ifndef QT_NO_SSL
+ QSignalSpy peerVerifyErrorSpy(&server, SIGNAL(peerVerifyError(QSslError)));
+ QSignalSpy sslErrorsSpy(&server, SIGNAL(sslErrors(QList<QSslError>)));
+#endif
+ QWebSocket socket;
+ QSignalSpy socketConnectedSpy(&socket, SIGNAL(connected()));
+
+ QVERIFY(server.listen());
+ QCOMPARE(server.serverAddress(), QHostAddress(QHostAddress::Any));
+ QCOMPARE(server.serverUrl(), QUrl(QStringLiteral("ws://") + QHostAddress(QHostAddress::LocalHost).toString() +
+ QStringLiteral(":").append(QString::number(server.serverPort()))));
+
+ socket.open(server.serverUrl().toString());
+
+ if (socketConnectedSpy.count() == 0)
+ QVERIFY(socketConnectedSpy.wait());
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(serverConnectionSpy.count(), 1);
+ QCOMPARE(corsAuthenticationSpy.count(), 1);
+
+ QCOMPARE(serverClosedSpy.count(), 0);
+
+ server.close();
+
+ QVERIFY(serverClosedSpy.wait());
+ QCOMPARE(serverClosedSpy.count(), 1);
+#ifndef QT_NO_SSL
+ QCOMPARE(peerVerifyErrorSpy.count(), 0);
+ QCOMPARE(sslErrorsSpy.count(), 0);
+#endif
+ QCOMPARE(serverErrorSpy.count(), 0);
+}
+
+void tst_QWebSocketServer::tst_maxPendingConnections()
+{
+ //tests if maximum connections are respected
+ //also checks if there are no side-effects like signals that are unexpectedly thrown
+ QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
+ server.setMaxPendingConnections(2);
+ QSignalSpy serverConnectionSpy(&server, SIGNAL(newConnection()));
+ QSignalSpy serverErrorSpy(&server,
+ SIGNAL(serverError(QWebSocketProtocol::CloseCode)));
+ QSignalSpy corsAuthenticationSpy(&server,
+ SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)));
+ QSignalSpy serverClosedSpy(&server, SIGNAL(closed()));
+#ifndef QT_NO_SSL
+ QSignalSpy peerVerifyErrorSpy(&server, SIGNAL(peerVerifyError(QSslError)));
+ QSignalSpy sslErrorsSpy(&server, SIGNAL(sslErrors(QList<QSslError>)));
+#endif
+ QSignalSpy serverAcceptErrorSpy(&server, SIGNAL(acceptError(QAbstractSocket::SocketError)));
+
+ QWebSocket socket1;
+ QWebSocket socket2;
+ QWebSocket socket3;
+
+ QSignalSpy socket1ConnectedSpy(&socket1, SIGNAL(connected()));
+ QSignalSpy socket2ConnectedSpy(&socket2, SIGNAL(connected()));
+ QSignalSpy socket3ConnectedSpy(&socket3, SIGNAL(connected()));
+
+ QVERIFY(server.listen());
+
+ socket1.open(server.serverUrl().toString());
+
+ if (socket1ConnectedSpy.count() == 0)
+ QVERIFY(socket1ConnectedSpy.wait());
+ QCOMPARE(socket1.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(serverConnectionSpy.count(), 1);
+ QCOMPARE(corsAuthenticationSpy.count(), 1);
+ socket2.open(server.serverUrl().toString());
+ if (socket2ConnectedSpy.count() == 0)
+ QVERIFY(socket2ConnectedSpy.wait());
+ QCOMPARE(socket2.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(serverConnectionSpy.count(), 2);
+ QCOMPARE(corsAuthenticationSpy.count(), 2);
+ socket3.open(server.serverUrl().toString());
+ if (socket3ConnectedSpy.count() == 0)
+ QVERIFY(!socket3ConnectedSpy.wait(250));
+ QCOMPARE(socket3.state(), QAbstractSocket::UnconnectedState);
+ QCOMPARE(serverConnectionSpy.count(), 2);
+ QCOMPARE(corsAuthenticationSpy.count(), 2);
+
+ QVERIFY(server.hasPendingConnections());
+ QWebSocket *pSocket = server.nextPendingConnection();
+ QVERIFY(pSocket);
+ delete pSocket;
+ QVERIFY(server.hasPendingConnections());
+ pSocket = server.nextPendingConnection();
+ QVERIFY(pSocket);
+ delete pSocket;
+ QVERIFY(!server.hasPendingConnections());
+ QVERIFY(!server.nextPendingConnection());
+
+//will resolve in another commit
+#ifndef Q_OS_WIN
+ QCOMPARE(serverErrorSpy.count(), 1);
+ QCOMPARE(serverErrorSpy.at(0).at(0).value<QWebSocketProtocol::CloseCode>(),
+ QWebSocketProtocol::CloseCodeAbnormalDisconnection);
+#endif
+ QCOMPARE(serverClosedSpy.count(), 0);
+
+ server.close();
+
+ QVERIFY(serverClosedSpy.wait());
+ QCOMPARE(serverClosedSpy.count(), 1);
+#ifndef QT_NO_SSL
+ QCOMPARE(peerVerifyErrorSpy.count(), 0);
+ QCOMPARE(sslErrorsSpy.count(), 0);
+#endif
+ QCOMPARE(serverAcceptErrorSpy.count(), 0);
+}
+
+void tst_QWebSocketServer::tst_serverDestroyedWhileSocketConnected()
+{
+ QWebSocketServer * server = new QWebSocketServer(QString(), QWebSocketServer::NonSecureMode);
+ QSignalSpy serverConnectionSpy(server, SIGNAL(newConnection()));
+ QSignalSpy corsAuthenticationSpy(server,
+ SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)));
+ QSignalSpy serverClosedSpy(server, SIGNAL(closed()));
+
+ QWebSocket socket;
+ QSignalSpy socketConnectedSpy(&socket, SIGNAL(connected()));
+ QSignalSpy socketDisconnectedSpy(&socket, SIGNAL(disconnected()));
+
+ QVERIFY(server->listen());
+ QCOMPARE(server->serverAddress(), QHostAddress(QHostAddress::Any));
+ QCOMPARE(server->serverUrl(), QUrl(QStringLiteral("ws://") + QHostAddress(QHostAddress::LocalHost).toString() +
+ QStringLiteral(":").append(QString::number(server->serverPort()))));
+
+ socket.open(server->serverUrl().toString());
+
+ if (socketConnectedSpy.count() == 0)
+ QVERIFY(socketConnectedSpy.wait());
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(serverConnectionSpy.count(), 1);
+ QCOMPARE(corsAuthenticationSpy.count(), 1);
+
+ QCOMPARE(serverClosedSpy.count(), 0);
+
+ delete server;
+
+ if (socketDisconnectedSpy.count() == 0)
+ QVERIFY(socketDisconnectedSpy.wait());
+ QCOMPARE(socketDisconnectedSpy.count(), 1);
+}
+
+QTEST_MAIN(tst_QWebSocketServer)
+
+#include "tst_qwebsocketserver.moc"
diff --git a/tests/auto/websockets/websocketframe/tst_websocketframe.cpp b/tests/auto/websockets/websocketframe/tst_websocketframe.cpp
new file mode 100644
index 0000000..5db82d5
--- /dev/null
+++ b/tests/auto/websockets/websocketframe/tst_websocketframe.cpp
@@ -0,0 +1,624 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QtTest/qtestcase.h>
+#include <QDebug>
+#include <QByteArray>
+#include <QtEndian>
+
+#include "private/qwebsocketframe_p.h"
+#include "private/qwebsocketprotocol_p.h"
+#include "qwebsocketprotocol.h"
+
+QT_USE_NAMESPACE
+
+Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
+Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
+
+/*!
+ * \brief class FrameHelper is used to encode a single frame.
+ *
+ * \internal
+ */
+class FrameHelper
+{
+public:
+ FrameHelper();
+
+ QByteArray wireRepresentation();
+
+ void setRsv1(int value) { m_rsv1 = value; }
+ void setRsv2(int value) { m_rsv2 = value; }
+ void setRsv3(int value) { m_rsv3 = value; }
+ void setMask(quint32 mask) { m_mask = mask; }
+ void setOpCode(QWebSocketProtocol::OpCode opCode) { m_opCode = opCode; }
+ void setPayload(const QByteArray &payload) { m_payload = payload; }
+ void setFinalFrame(bool isFinal) { m_isFinalFrame = isFinal; }
+
+private:
+ int m_rsv1;
+ int m_rsv2;
+ int m_rsv3;
+ quint32 m_mask;
+ QWebSocketProtocol::OpCode m_opCode;
+ QByteArray m_payload;
+ bool m_isFinalFrame;
+};
+
+FrameHelper::FrameHelper() :
+ m_rsv1(0), m_rsv2(0), m_rsv3(0),
+ m_mask(0), m_opCode(QWebSocketProtocol::OpCodeReserved3),
+ m_payload(), m_isFinalFrame(false)
+{}
+
+QByteArray FrameHelper::wireRepresentation()
+{
+ quint8 byte = 0x00;
+ QByteArray wireRep;
+ quint64 payloadLength = m_payload.length();
+
+ //FIN, opcode
+ byte = static_cast<quint8>((m_opCode & 0x0F) | (m_isFinalFrame ? 0x80 : 0x00)); //FIN, opcode
+ //RSV1-3
+ byte |= static_cast<quint8>(((m_rsv1 & 0x01) << 6) | ((m_rsv2 & 0x01) << 5) |
+ ((m_rsv3 & 0x01) << 4));
+ wireRep.append(static_cast<char>(byte));
+
+ byte = 0x00;
+ if (m_mask != 0)
+ {
+ byte |= 0x80;
+ }
+ if (payloadLength <= 125)
+ {
+ byte |= static_cast<quint8>(payloadLength);
+ wireRep.append(static_cast<char>(byte));
+ }
+ else if (payloadLength <= 0xFFFFU)
+ {
+ byte |= 126;
+ wireRep.append(static_cast<char>(byte));
+ quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
+ wireRep.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
+ }
+ else
+ {
+ byte |= 127;
+ wireRep.append(static_cast<char>(byte));
+ quint64 swapped = qToBigEndian<quint64>(payloadLength);
+ wireRep.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
+ }
+ //Write mask
+ if (m_mask != 0)
+ {
+ wireRep.append(static_cast<const char *>(static_cast<const void *>(&m_mask)),
+ sizeof(quint32));
+ }
+ QByteArray tmpData = m_payload;
+ if (m_mask)
+ {
+ tmpData.detach();
+ QWebSocketProtocol::mask(&tmpData, m_mask);
+ }
+ wireRep.append(tmpData);
+ return wireRep;
+}
+
+class tst_WebSocketFrame : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_WebSocketFrame();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void tst_initialization();
+ void tst_copyConstructorAndAssignment();
+
+ void tst_goodFrames_data();
+ void tst_goodFrames();
+
+ void tst_invalidFrames_data();
+ void tst_invalidFrames();
+
+ void tst_malformedFrames_data();
+ void tst_malformedFrames();
+};
+
+tst_WebSocketFrame::tst_WebSocketFrame()
+{}
+
+void tst_WebSocketFrame::initTestCase()
+{
+}
+
+void tst_WebSocketFrame::cleanupTestCase()
+{}
+
+void tst_WebSocketFrame::init()
+{
+ qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode");
+ qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
+}
+
+void tst_WebSocketFrame::cleanup()
+{
+}
+
+void tst_WebSocketFrame::tst_initialization()
+{
+ QWebSocketFrame frame;
+ QVERIFY(!frame.isValid());
+ QCOMPARE(frame.payload().length(), 0);
+}
+
+void tst_WebSocketFrame::tst_copyConstructorAndAssignment()
+{
+ FrameHelper frameHelper;
+ frameHelper.setRsv1(0);
+ frameHelper.setRsv2(0);
+ frameHelper.setRsv3(0);
+ frameHelper.setFinalFrame(true);
+ frameHelper.setMask(1234u);
+ frameHelper.setOpCode(QWebSocketProtocol::OpCodeBinary);
+ frameHelper.setPayload(QByteArrayLiteral("12345"));
+
+ QByteArray payload = frameHelper.wireRepresentation();
+ QBuffer buffer(&payload);
+ buffer.open(QIODevice::ReadOnly);
+
+ QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
+ buffer.close();
+
+ {
+ QWebSocketFrame other(frame);
+ QCOMPARE(other.closeCode(), frame.closeCode());
+ QCOMPARE(other.closeReason(), frame.closeReason());
+ QCOMPARE(other.hasMask(), frame.hasMask());
+ QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
+ QCOMPARE(other.isControlFrame(), frame.isControlFrame());
+ QCOMPARE(other.isDataFrame(), frame.isDataFrame());
+ QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
+ QCOMPARE(other.isValid(), frame.isValid());
+ QCOMPARE(other.mask(), frame.mask());
+ QCOMPARE(other.opCode(), frame.opCode());
+ QCOMPARE(other.payload(), frame.payload());
+ QCOMPARE(other.rsv1(), frame.rsv1());
+ QCOMPARE(other.rsv2(), frame.rsv2());
+ QCOMPARE(other.rsv3(), frame.rsv3());
+ }
+ {
+ QWebSocketFrame other;
+ other = frame;
+ QCOMPARE(other.closeCode(), frame.closeCode());
+ QCOMPARE(other.closeReason(), frame.closeReason());
+ QCOMPARE(other.hasMask(), frame.hasMask());
+ QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
+ QCOMPARE(other.isControlFrame(), frame.isControlFrame());
+ QCOMPARE(other.isDataFrame(), frame.isDataFrame());
+ QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
+ QCOMPARE(other.isValid(), frame.isValid());
+ QCOMPARE(other.mask(), frame.mask());
+ QCOMPARE(other.opCode(), frame.opCode());
+ QCOMPARE(other.payload(), frame.payload());
+ QCOMPARE(other.rsv1(), frame.rsv1());
+ QCOMPARE(other.rsv2(), frame.rsv2());
+ QCOMPARE(other.rsv3(), frame.rsv3());
+ }
+}
+
+void tst_WebSocketFrame::tst_goodFrames_data()
+{
+ QTest::addColumn<int>("rsv1");
+ QTest::addColumn<int>("rsv2");
+ QTest::addColumn<int>("rsv3");
+ QTest::addColumn<quint32>("mask");
+ QTest::addColumn<QWebSocketProtocol::OpCode>("opCode");
+ QTest::addColumn<bool>("isFinal");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<bool>("isControlFrame");
+ QTest::addColumn<bool>("isDataFrame");
+ QTest::addColumn<bool>("isContinuationFrame");
+
+ QTest::newRow("Non masked final text frame with small payload")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeText
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << false << true << false;
+ QTest::newRow("Non masked final binary frame with small payload")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeBinary
+ << true << QByteArrayLiteral("\x00\x01\x02\x03\x04")
+ << false << true << false;
+ QTest::newRow("Non masked final text frame with no payload")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeText
+ << true << QByteArray()
+ << false << true << false;
+ QTest::newRow("Non masked final binary frame with no payload")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeBinary
+ << true << QByteArray()
+ << false << true << false;
+
+ QTest::newRow("Non masked final close frame with small payload")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeClose
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << true << false << false;
+ QTest::newRow("Non masked final close frame with no payload")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeClose
+ << true << QByteArray()
+ << true << false << false;
+ QTest::newRow("Non masked final ping frame with small payload")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodePing
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << true << false << false;
+ QTest::newRow("Non masked final pong frame with no payload")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodePong
+ << true << QByteArray()
+ << true << false << false;
+
+ QTest::newRow("Non masked final continuation frame with small payload")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeContinue
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << false << true << true;
+ QTest::newRow("Non masked non-final continuation frame with small payload")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeContinue
+ << false << QStringLiteral("Hello world!").toUtf8()
+ << false << true << true;
+}
+
+void tst_WebSocketFrame::tst_goodFrames()
+{
+ QFETCH(int, rsv1);
+ QFETCH(int, rsv2);
+ QFETCH(int, rsv3);
+ QFETCH(quint32, mask);
+ QFETCH(QWebSocketProtocol::OpCode, opCode);
+ QFETCH(bool, isFinal);
+ QFETCH(QByteArray, payload);
+ QFETCH(bool, isControlFrame);
+ QFETCH(bool, isDataFrame);
+ QFETCH(bool, isContinuationFrame);
+
+ FrameHelper helper;
+ helper.setRsv1(rsv1);
+ helper.setRsv2(rsv2);
+ helper.setRsv3(rsv3);
+ helper.setMask(mask);
+ helper.setOpCode(opCode);
+ helper.setFinalFrame(isFinal);
+ helper.setPayload(payload);
+
+ QByteArray wireRepresentation = helper.wireRepresentation();
+ QBuffer buffer;
+ buffer.setData(wireRepresentation);
+ buffer.open(QIODevice::ReadOnly);
+ QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
+ buffer.close();
+ QVERIFY(frame.isValid());
+ QCOMPARE(frame.rsv1(), rsv1);
+ QCOMPARE(frame.rsv2(), rsv2);
+ QCOMPARE(frame.rsv3(), rsv3);
+ QCOMPARE(frame.hasMask(), (mask != 0));
+ QCOMPARE(frame.opCode(), opCode);
+ QCOMPARE(frame.isFinalFrame(), isFinal);
+ QCOMPARE(frame.isControlFrame(), isControlFrame);
+ QCOMPARE(frame.isDataFrame(), isDataFrame);
+ QCOMPARE(frame.isContinuationFrame(), isContinuationFrame);
+ QCOMPARE(frame.payload().length(), payload.length());
+ QCOMPARE(frame.payload(), payload);
+}
+
+void tst_WebSocketFrame::tst_invalidFrames_data()
+{
+ QTest::addColumn<int>("rsv1");
+ QTest::addColumn<int>("rsv2");
+ QTest::addColumn<int>("rsv3");
+ QTest::addColumn<quint32>("mask");
+ QTest::addColumn<QWebSocketProtocol::OpCode>("opCode");
+ QTest::addColumn<bool>("isFinal");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedError");
+
+ QTest::newRow("RSV1 != 0")
+ << 1 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeText
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("RSV2 != 0")
+ << 0 << 1 << 0
+ << 0U << QWebSocketProtocol::OpCodeText
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("RSV3 != 0")
+ << 0 << 0 << 1
+ << 0U << QWebSocketProtocol::OpCodeText
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("RSV1 != 0 and RSV2 != 0")
+ << 1 << 1 << 0
+ << 0U << QWebSocketProtocol::OpCodeText
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("RSV1 != 0 and RSV3 != 0")
+ << 1 << 0 << 1
+ << 0U << QWebSocketProtocol::OpCodeText
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("RSV2 != 0 and RSV3 != 0")
+ << 0 << 1 << 1
+ << 0U << QWebSocketProtocol::OpCodeText
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+
+ QTest::newRow("Reserved OpCode 3")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeReserved3
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Reserved OpCode 4")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeReserved4
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Reserved OpCode 5")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeReserved5
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Reserved OpCode 6")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeReserved6
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Reserved OpCode 7")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeReserved7
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Reserved OpCode B")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeReservedB
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Reserved OpCode C")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeReservedC
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Reserved OpCode D")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeReservedD
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Reserved OpCode E")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeReservedE
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Reserved OpCode F")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeReservedF
+ << true << QStringLiteral("Hello world!").toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+
+ QTest::newRow("Close Frame with payload > 125 bytes")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeClose
+ << true << QString(126, 'a').toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Non-final Close Frame")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeClose
+ << false << QString(126, 'a').toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Ping Frame with payload > 125 bytes")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodePing
+ << true << QString(126, 'a').toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Non-final Ping Frame")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodePing
+ << false << QString(126, 'a').toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Pong Frame with payload > 125 bytes")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodePong
+ << true << QString(126, 'a').toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ QTest::newRow("Non-final Pong Frame")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodePong
+ << false << QString(126, 'a').toUtf8()
+ << QWebSocketProtocol::CloseCodeProtocolError;
+}
+
+void tst_WebSocketFrame::tst_invalidFrames()
+{
+ QFETCH(int, rsv1);
+ QFETCH(int, rsv2);
+ QFETCH(int, rsv3);
+ QFETCH(quint32, mask);
+ QFETCH(QWebSocketProtocol::OpCode, opCode);
+ QFETCH(bool, isFinal);
+ QFETCH(QByteArray, payload);
+ QFETCH(QWebSocketProtocol::CloseCode, expectedError);
+
+ FrameHelper helper;
+ helper.setRsv1(rsv1);
+ helper.setRsv2(rsv2);
+ helper.setRsv3(rsv3);
+ helper.setMask(mask);
+ helper.setOpCode(opCode);
+ helper.setFinalFrame(isFinal);
+ helper.setPayload(payload);
+
+ QByteArray wireRepresentation = helper.wireRepresentation();
+ QBuffer buffer;
+ buffer.setData(wireRepresentation);
+ buffer.open(QIODevice::ReadOnly);
+ QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
+ buffer.close();
+
+ QVERIFY(!frame.isValid());
+ QCOMPARE(frame.closeCode(), expectedError);
+}
+
+
+/*
+ * Incomplete or overly large frames
+ * Payload must be crafted manually
+ *
+ QTest::newRow("Frame Too Big")
+ << 0 << 0 << 0
+ << 0U << QWebSocketProtocol::OpCodeText
+ << true << QString(MAX_FRAME_SIZE_IN_BYTES + 1, 'a').toUtf8()
+ << QWebSocketProtocol::CloseCodeTooMuchData;
+
+ */
+void tst_WebSocketFrame::tst_malformedFrames_data()
+{
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedError");
+
+ //too little data
+ QTest::newRow("No data") << QByteArray() << QWebSocketProtocol::CloseCodeGoingAway;
+ FrameHelper helper;
+ helper.setRsv1(0);
+ helper.setRsv2(0);
+ helper.setRsv3(0);
+ helper.setMask(0U);
+ helper.setOpCode(QWebSocketProtocol::OpCodeText);
+ helper.setFinalFrame(true);
+ helper.setPayload(QString(10, 'a').toUtf8());
+ QByteArray wireRep = helper.wireRepresentation();
+
+ //too little data
+ //header + payload should be 12 bytes for non-masked payloads < 126 bytes
+ for (int i = 1; i < 12; ++i)
+ {
+ QTest::newRow(QStringLiteral("Header too small - %1 byte(s)").arg(i).toLatin1().constData())
+ << wireRep.left(i)
+ << QWebSocketProtocol::CloseCodeGoingAway;
+ }
+ //too much data
+ {
+ const char bigpayloadIndicator = char(127);
+ const quint64 payloadSize = MAX_FRAME_SIZE_IN_BYTES + 1;
+ uchar swapped[8] = {0};
+ qToBigEndian<quint64>(payloadSize, swapped);
+ QTest::newRow("Frame too big")
+ << wireRep.left(1).append(bigpayloadIndicator)
+ .append(reinterpret_cast<char *>(swapped), 8)
+ << QWebSocketProtocol::CloseCodeTooMuchData;
+ }
+ //invalid size field
+ {
+ const char bigpayloadIndicator = char(127);
+ quint64 payloadSize = quint64(1) << 63;
+ uchar swapped[8] = {0};
+ qToBigEndian<quint64>(payloadSize, swapped);
+ QTest::newRow("Highest bit of payload length is set")
+ << wireRep.left(1).append(bigpayloadIndicator)
+ .append(reinterpret_cast<char *>(swapped), 8)
+ << QWebSocketProtocol::CloseCodeProtocolError;
+
+ payloadSize = 256;
+ qToBigEndian<quint64>(payloadSize, swapped);
+ QTest::newRow("Overlong 64-bit size field; should be 16-bit")
+ << wireRep.left(1).append(bigpayloadIndicator)
+ .append(reinterpret_cast<char *>(swapped), 8)
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ }
+ //overlong size field
+ {
+ const char largepayloadIndicator = char(126);
+ const quint16 payloadSize = 120;
+ uchar swapped[2] = {0};
+ qToBigEndian<quint16>(payloadSize, swapped);
+ QTest::newRow("Overlong 16-bit size field")
+ << wireRep.left(1).append(largepayloadIndicator)
+ .append(reinterpret_cast<char *>(swapped), 2)
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ }
+ {
+ const char bigpayloadIndicator = char(127);
+ quint64 payloadSize = 120;
+ uchar swapped[8] = {0};
+ qToBigEndian<quint64>(payloadSize, swapped);
+ QTest::newRow("Overlong 64-bit size field; should be 7-bit")
+ << wireRep.left(1).append(bigpayloadIndicator)
+ .append(reinterpret_cast<char *>(swapped), 8)
+ << QWebSocketProtocol::CloseCodeProtocolError;
+
+ payloadSize = 256;
+ qToBigEndian<quint64>(payloadSize, swapped);
+ QTest::newRow("Overlong 64-bit size field; should be 16-bit")
+ << wireRep.left(1).append(bigpayloadIndicator)
+ .append(reinterpret_cast<char *>(swapped), 8)
+ << QWebSocketProtocol::CloseCodeProtocolError;
+ }
+}
+
+void tst_WebSocketFrame::tst_malformedFrames()
+{
+ QFETCH(QByteArray, payload);
+ QFETCH(QWebSocketProtocol::CloseCode, expectedError);
+
+ QBuffer buffer;
+ buffer.setData(payload);
+ buffer.open(QIODevice::ReadOnly);
+ QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
+ buffer.close();
+
+ QVERIFY(!frame.isValid());
+ QCOMPARE(frame.closeCode(), expectedError);
+}
+
+QTEST_MAIN(tst_WebSocketFrame)
+
+#include "tst_websocketframe.moc"
+
diff --git a/tests/auto/websockets/websocketframe/websocketframe.pro b/tests/auto/websockets/websocketframe/websocketframe.pro
new file mode 100644
index 0000000..9682348
--- /dev/null
+++ b/tests/auto/websockets/websocketframe/websocketframe.pro
@@ -0,0 +1,14 @@
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+TARGET = tst_websocketframe
+
+QT = core testlib websockets websockets-private
+
+SOURCES += tst_websocketframe.cpp
+
+requires(contains(QT_CONFIG, private_tests))
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/websockets/websocketprotocol/tst_websocketprotocol.cpp b/tests/auto/websockets/websocketprotocol/tst_websocketprotocol.cpp
new file mode 100644
index 0000000..94816de
--- /dev/null
+++ b/tests/auto/websockets/websocketprotocol/tst_websocketprotocol.cpp
@@ -0,0 +1,289 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QtTest/qtestcase.h>
+#include <QtEndian>
+
+#include <QDebug>
+
+#include "QtWebSockets/qwebsocketprotocol.h"
+#include "private/qwebsocketprotocol_p.h"
+
+QT_USE_NAMESPACE
+
+Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
+Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
+Q_DECLARE_METATYPE(QWebSocketProtocol::Version)
+
+class tst_WebSocketProtocol : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_WebSocketProtocol();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void tst_validMasks_data();
+ void tst_validMasks();
+
+ void tst_opCodes_data();
+ void tst_opCodes();
+
+ void tst_closeCodes_data();
+ void tst_closeCodes();
+
+ void tst_versionFromString_data();
+ void tst_versionFromString();
+};
+
+tst_WebSocketProtocol::tst_WebSocketProtocol()
+{}
+
+void tst_WebSocketProtocol::initTestCase()
+{
+}
+
+void tst_WebSocketProtocol::cleanupTestCase()
+{}
+
+void tst_WebSocketProtocol::init()
+{
+ qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode");
+ qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
+}
+
+void tst_WebSocketProtocol::cleanup()
+{
+}
+
+void tst_WebSocketProtocol::tst_validMasks_data()
+{
+ QTest::addColumn<quint32>("mask");
+ QTest::addColumn<QString>("inputdata");
+ QTest::addColumn<QByteArray>("result");
+
+ QTest::newRow("Empty payload") << 0x12345678u << QString() << QByteArray();
+ QTest::newRow("ASCII payload of 8 characters")
+ << 0x12345678u
+ << QStringLiteral("abcdefgh")
+ << QByteArrayLiteral("\x73\x56\x35\x1C\x77\x52\x31\x10");
+ QTest::newRow("ASCII payload of 9 characters")
+ << 0x12345678u
+ << QStringLiteral("abcdefghi")
+ << QByteArrayLiteral("\x73\x56\x35\x1C\x77\x52\x31\x10\x7B");
+ //MSVC doesn't like UTF-8 in source code;
+ //the following text is represented in the string below: ∫∂ƒ©øØ
+ QTest::newRow("UTF-8 payload")
+ << 0x12345678u
+ << QString::fromUtf8("\xE2\x88\xAB\xE2\x88\x82\xC6\x92\xC2\xA9\xC3\xB8\xC3\x98")
+ << QByteArrayLiteral("\x2D\x0B\x69\xD1\xEA\xEC");
+}
+
+void tst_WebSocketProtocol::tst_validMasks()
+{
+ QFETCH(quint32, mask);
+ QFETCH(QString, inputdata);
+ QFETCH(QByteArray, result);
+
+ //put latin1 into an explicit array
+ //otherwise, the intermediate object is deleted and the data pointer becomes invalid
+ QByteArray latin1 = inputdata.toLatin1();
+ char *data = latin1.data();
+
+ QWebSocketProtocol::mask(data, inputdata.size(), mask);
+ QCOMPARE(QByteArray::fromRawData(data, inputdata.size()), result);
+}
+
+void tst_WebSocketProtocol::tst_opCodes_data()
+{
+ QTest::addColumn<QWebSocketProtocol::OpCode>("opCode");
+ QTest::addColumn<bool>("isReserved");
+
+ QTest::newRow("OpCodeBinary") << QWebSocketProtocol::OpCodeBinary << false;
+ QTest::newRow("OpCodeClose") << QWebSocketProtocol::OpCodeClose << false;
+ QTest::newRow("OpCodeContinue") << QWebSocketProtocol::OpCodeContinue << false;
+ QTest::newRow("OpCodePing") << QWebSocketProtocol::OpCodePing << false;
+ QTest::newRow("OpCodePong") << QWebSocketProtocol::OpCodePong << false;
+ QTest::newRow("OpCodeReserved3") << QWebSocketProtocol::OpCodeReserved3 << true;
+ QTest::newRow("OpCodeReserved4") << QWebSocketProtocol::OpCodeReserved4 << true;
+ QTest::newRow("OpCodeReserved5") << QWebSocketProtocol::OpCodeReserved5 << true;
+ QTest::newRow("OpCodeReserved6") << QWebSocketProtocol::OpCodeReserved6 << true;
+ QTest::newRow("OpCodeReserved7") << QWebSocketProtocol::OpCodeReserved7 << true;
+ QTest::newRow("OpCodeReserved8") << QWebSocketProtocol::OpCodeReservedB << true;
+ QTest::newRow("OpCodeReservedC") << QWebSocketProtocol::OpCodeReservedC << true;
+ QTest::newRow("OpCodeReservedD") << QWebSocketProtocol::OpCodeReservedD << true;
+ QTest::newRow("OpCodeReservedE") << QWebSocketProtocol::OpCodeReservedE << true;
+ QTest::newRow("OpCodeReservedF") << QWebSocketProtocol::OpCodeReservedF << true;
+ QTest::newRow("OpCodeText") << QWebSocketProtocol::OpCodeText << false;
+}
+
+void tst_WebSocketProtocol::tst_opCodes()
+{
+ QFETCH(QWebSocketProtocol::OpCode, opCode);
+ QFETCH(bool, isReserved);
+
+ bool result = QWebSocketProtocol::isOpCodeReserved(opCode);
+
+ QCOMPARE(result, isReserved);
+}
+
+void tst_WebSocketProtocol::tst_closeCodes_data()
+{
+ QTest::addColumn<int>("closeCode");
+ QTest::addColumn<bool>("isValid");
+
+ for (int i = 0; i < 1000; ++i)
+ {
+ QTest::newRow(QStringLiteral("Close code %1").arg(i).toLatin1().constData()) << i << false;
+ }
+
+ for (int i = 1000; i < 1004; ++i)
+ {
+ QTest::newRow(QStringLiteral("Close code %1").arg(i).toLatin1().constData()) << i << true;
+ }
+
+ QTest::newRow("Close code 1004") << 1004 << false;
+ QTest::newRow("Close code 1005") << 1005 << false;
+ QTest::newRow("Close code 1006") << 1006 << false;
+
+ for (int i = 1007; i < 1012; ++i)
+ {
+ QTest::newRow(QStringLiteral("Close code %1").arg(i).toLatin1().constData()) << i << true;
+ }
+
+ for (int i = 1013; i < 3000; ++i)
+ {
+ QTest::newRow(QStringLiteral("Close code %1").arg(i).toLatin1().constData()) << i << false;
+ }
+
+ for (int i = 3000; i < 5000; ++i)
+ {
+ QTest::newRow(QStringLiteral("Close code %1").arg(i).toLatin1().constData()) << i << true;
+ }
+
+ QTest::newRow("Close code 5000") << 1004 << false;
+ QTest::newRow("Close code 6000") << 1004 << false;
+ QTest::newRow("Close code 7000") << 1004 << false;
+}
+
+void tst_WebSocketProtocol::tst_closeCodes()
+{
+ QFETCH(int, closeCode);
+ QFETCH(bool, isValid);
+
+ bool result = QWebSocketProtocol::isCloseCodeValid(closeCode);
+
+ QCOMPARE(result, isValid);
+}
+
+void tst_WebSocketProtocol::tst_versionFromString_data()
+{
+ QTest::addColumn<QWebSocketProtocol::Version>("version");
+ QTest::addColumn<QString>("versionString");
+
+ //happy flow; good data
+ QTest::newRow("Version 0")
+ << QWebSocketProtocol::Version0
+ << QStringLiteral("0");
+ QTest::newRow("Version 4")
+ << QWebSocketProtocol::Version4
+ << QStringLiteral("4");
+ QTest::newRow("Version 5")
+ << QWebSocketProtocol::Version5
+ << QStringLiteral("5");
+ QTest::newRow("Version 6")
+ << QWebSocketProtocol::Version6
+ << QStringLiteral("6");
+ QTest::newRow("Version 7")
+ << QWebSocketProtocol::Version7
+ << QStringLiteral("7");
+ QTest::newRow("Version 8")
+ << QWebSocketProtocol::Version8
+ << QStringLiteral("8");
+ QTest::newRow("Version 13")
+ << QWebSocketProtocol::Version13
+ << QStringLiteral("13");
+
+ //rainy flow; invalid data
+ QTest::newRow("Version -1")
+ << QWebSocketProtocol::VersionUnknown
+ << QStringLiteral("-1");
+ QTest::newRow("Version 1")
+ << QWebSocketProtocol::VersionUnknown
+ << QStringLiteral("1");
+ QTest::newRow("Version 2")
+ << QWebSocketProtocol::VersionUnknown
+ << QStringLiteral("2");
+ QTest::newRow("Version 3")
+ << QWebSocketProtocol::VersionUnknown
+ << QStringLiteral("3");
+ QTest::newRow("Version 9")
+ << QWebSocketProtocol::VersionUnknown
+ << QStringLiteral("9");
+ QTest::newRow("Version 10")
+ << QWebSocketProtocol::VersionUnknown
+ << QStringLiteral("10");
+ QTest::newRow("Version 11")
+ << QWebSocketProtocol::VersionUnknown
+ << QStringLiteral("11");
+ QTest::newRow("Version 12")
+ << QWebSocketProtocol::VersionUnknown
+ << QStringLiteral("12");
+ QTest::newRow("Version abcd")
+ << QWebSocketProtocol::VersionUnknown
+ << QStringLiteral("abcd");
+ QTest::newRow("Version 1.6")
+ << QWebSocketProtocol::VersionUnknown
+ << QStringLiteral("1.6");
+ QTest::newRow("Version empty")
+ << QWebSocketProtocol::VersionUnknown
+ << QString();
+}
+
+void tst_WebSocketProtocol::tst_versionFromString()
+{
+ QFETCH(QWebSocketProtocol::Version, version);
+ QFETCH(QString, versionString);
+
+ QCOMPARE(QWebSocketProtocol::versionFromString(versionString), version);
+}
+
+QTEST_MAIN(tst_WebSocketProtocol)
+
+#include "tst_websocketprotocol.moc"
+
diff --git a/tests/auto/websockets/websocketprotocol/websocketprotocol.pro b/tests/auto/websockets/websocketprotocol/websocketprotocol.pro
new file mode 100644
index 0000000..c21bb64
--- /dev/null
+++ b/tests/auto/websockets/websocketprotocol/websocketprotocol.pro
@@ -0,0 +1,14 @@
+CONFIG += console
+CONFIG += testcase
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+TARGET = tst_websocketprotocol
+
+QT = core testlib websockets websockets-private
+
+SOURCES += tst_websocketprotocol.cpp
+
+requires(contains(QT_CONFIG, private_tests))
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/websockets/websockets.pro b/tests/auto/websockets/websockets.pro
new file mode 100644
index 0000000..4698cd0
--- /dev/null
+++ b/tests/auto/websockets/websockets.pro
@@ -0,0 +1,16 @@
+TEMPLATE = subdirs
+
+SUBDIRS = \
+ qwebsocketcorsauthenticator
+
+contains(QT_CONFIG, private_tests): SUBDIRS += \
+ websocketprotocol \
+ dataprocessor \
+ websocketframe \
+ handshakerequest \
+ handshakeresponse \
+ qdefaultmaskgenerator
+
+SUBDIRS += \
+ qwebsocket \
+ qwebsocketserver