diff options
author | Kurt Pattyn <pattyn.kurt@gmail.com> | 2013-10-10 21:12:27 +0200 |
---|---|---|
committer | Kurt Pattyn <pattyn.kurt@gmail.com> | 2013-10-11 10:36:38 +0200 |
commit | b86f2949f63e0ae904c19fb6d39dc6793b164db9 (patch) | |
tree | 8823c24edfdda47fe273e141ddf3b705488dc2df | |
parent | db78c8db3cfc5400118f8f4dc81613cbacc477d1 (diff) | |
download | qtwebsockets-b86f2949f63e0ae904c19fb6d39dc6793b164db9.tar.gz |
Added checks for valid control frames
Also added extra checks on signals
Added extra comments
Cleaned up superfluous TODOs
Change-Id: I8ec760f0984bee4bd4cc757ce3ef6d7ed1c01102
Reviewed-by: Steven Ceuppens <steven.ceuppens@icloud.com>
-rw-r--r-- | src/websockets/qwebsocketserver.cpp | 3 | ||||
-rw-r--r-- | tests/auto/dataprocessor/tst_dataprocessor.cpp | 1375 |
2 files changed, 736 insertions, 642 deletions
diff --git a/src/websockets/qwebsocketserver.cpp b/src/websockets/qwebsocketserver.cpp index 1ea0f99..f267001 100644 --- a/src/websockets/qwebsocketserver.cpp +++ b/src/websockets/qwebsocketserver.cpp @@ -107,9 +107,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "qwebsocketserver.h" #include "qwebsocketserver_p.h" -//TODO: CorsCheck: give list in constructor or use CorsAuthenticator object -//in QNetworkAccessManager the signal cannot be connected to a queued signal, because it waits for the signal to return - QT_BEGIN_NAMESPACE /*! diff --git a/tests/auto/dataprocessor/tst_dataprocessor.cpp b/tests/auto/dataprocessor/tst_dataprocessor.cpp index fac155d..4fd14fe 100644 --- a/tests/auto/dataprocessor/tst_dataprocessor.cpp +++ b/tests/auto/dataprocessor/tst_dataprocessor.cpp @@ -14,29 +14,16 @@ Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode) //TODO: test valid UTF8 sequences (see UC 6.2) //TODO: test on masking correctness //TODO: test for valid fields -//TODO: test for valid control frames -//DONE: test for valid close codes -//DONE: test close frame with no close code and reason //TODO: test if opcode is correct after processing of a continuation frame (text and binary frames) //TODO: test valid frame sequences //rainy-day flow -//DONE: test invalid UTF8 sequences -//DONE: test invalid UTF8 sequences in control/close frames //TODO: test invalid masks //TODO: test for AutoBahn testcase 5 //TODO: test for AutoBahn testcase 6.1 //TODO: test for AutoBahn testcase 6.3 (fragmentation test) //TODO: test for AutoBahn testcase 6.4 (fragmentation test) -//DONE: test for invalid fields -//DONE: test for invalid opcodes -//DONE: test continuation frames for too big payload -//DONE: test continuation frames for too small frame -//DONE: test continuation frames for bad rsv fields, etc. -//DONE: test continuation frames for incomplete payload -//DONE: test close frame with payload length 1 (is either 0, if no close code, or at least 2, close code + optional reason) -//DONE: besides spying on errors, we should also check if the frame and message signals are not emitted (or partially emitted) //TODO: test invalid frame sequences @@ -68,20 +55,24 @@ private Q_SLOTS: \brief Tests all kinds of valid binary frames, including zero length frames */ void goodBinaryFrame(); + void goodBinaryFrame_data(); /*! \brief Tests all kinds of valid text frames, including zero length frames */ void goodTextFrame(); + void goodTextFrame_data(); /*! * \brief Test all kinds of valid control frames. */ - //void goodControlFrame(); + void goodControlFrame(); + /*! * \brief Test all kinds of valid close frames. */ void goodCloseFrame(); + void goodCloseFrame_data(); //void goodHeaders(); //test all valid opcodes @@ -92,9 +83,10 @@ private Q_SLOTS: to allow non-characters again. */ void nonCharacterCodes(); + void nonCharacterCodes_data(); /*************************************************************************** - * Unhappy Flows + * Rainy Day Flows ***************************************************************************/ /*! \brief Tests the QWebSocketDataProcessor for correct handling of frames that don't contain the starting 2 bytes. @@ -108,12 +100,14 @@ private Q_SLOTS: This test does not test sequences of frames, only single frames are tested */ void frameTooBig(); + void frameTooBig_data(); /*! \brief 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(); /*! \brief Tests the QWebSocketDataProcessor for the correct handling of invalid control frames. @@ -121,12 +115,16 @@ private Q_SLOTS: This test does not test sequences of frames, only single frames are tested */ void invalidControlFrame(); + void invalidControlFrame_data(); + void invalidCloseFrame(); + void invalidCloseFrame_data(); /*! \brief Tests the QWebSocketDataProcessor for the correct handling of incomplete size fields for large and big payloads. */ void incompleteSizeField(); + void incompleteSizeField_data(); /*! \brief Tests the QWebSocketDataProcessor for the correct handling of incomplete payloads. @@ -138,34 +136,23 @@ private Q_SLOTS: This test does not test sequences of frames, only single frames are tested */ void incompletePayload(); + void incompletePayload_data(); /*! \brief 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 invalidHeader_data(); - void incompleteSizeField_data(); - void incompletePayload_data(); - void invalidPayload_data(bool isControlFrame = false); - void invalidPayloadInCloseFrame_data(); void minimumSizeRequirement_data(); - void invalidControlFrame_data(); - void invalidCloseFrame_data(); - void frameTooBig_data(); - - void nonCharacterCodes_data(); - void goodTextFrame_data(); - void goodBinaryFrame_data(); - void goodCloseFrame_data(); private: //helper function that constructs a new row of test data for invalid UTF8 sequences @@ -210,80 +197,19 @@ void tst_DataProcessor::cleanup() { } -void tst_DataProcessor::goodTextFrame_data() -{ - QTest::addColumn<QByteArray>("payload"); - - //test frames with small (< 126), large ( < 65536) and big ( > 65535) payloads - for (int i = 0; i < (65536 + 256); i += 128) - { - QTest::newRow(QString("Text frame with %1 ASCII characters").arg(i).toStdString().data()) << QByteArray(i, 'a'); - } - //test all valid ASCII characters - for (int i = 0; i < 128; ++i) - { - QTest::newRow(QString("Text frame with containing ASCII character '0x%1'").arg(QByteArray(1, char(i)).toHex().constData()).toStdString().data()) << QByteArray(1, char(i)); - } -} - void tst_DataProcessor::goodBinaryFrame_data() { QTest::addColumn<QByteArray>("payload"); - for (int i = 0; i < (65536 + 256); i += 128) + for (int i = 0; i < (65536 + 256); i += 128) //be sure to get small (< 126 bytes), large (> 125 bytes & < 64K) and big (>64K) frames { QTest::newRow(QString("Binary frame with %1 bytes").arg(i).toStdString().data()) << QByteArray(i, char(1)); } - for (int i = 0; i < 256; ++i) + for (int i = 0; i < 256; ++i) //test all possible bytes in the payload { QTest::newRow(QString("Binary frame containing byte: '0x%1'").arg(QByteArray(1, char(i)).toHex().constData()).toStdString().data()) << QByteArray(i, char(1)); } } -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(QString("Close frame with %1 ASCII characters").arg(i).toStdString().data()) << QString(i, 'a') << QWebSocketProtocol::CC_NORMAL; - } - for (int i = 0; i < 126; ++i) - { - QTest::newRow(QString("Text frame with containing ASCII character '0x%1'").arg(QByteArray(1, char(i)).toHex().constData()).toStdString().data()) << QString(1, char(i)) << QWebSocketProtocol::CC_NORMAL; - } - QTest::newRow("Close frame with close code NORMAL") << QString(1, 'a') << QWebSocketProtocol::CC_NORMAL; - QTest::newRow("Close frame with close code BAD OPERATION") << QString(1, 'a') << QWebSocketProtocol::CC_BAD_OPERATION; - QTest::newRow("Close frame with close code DATATYPE NOT SUPPORTED") << QString(1, 'a') << QWebSocketProtocol::CC_DATATYPE_NOT_SUPPORTED; - QTest::newRow("Close frame with close code GOING AWAY") << QString(1, 'a') << QWebSocketProtocol::CC_GOING_AWAY; - QTest::newRow("Close frame with close code MISSING EXTENSION") << QString(1, 'a') << QWebSocketProtocol::CC_MISSING_EXTENSION; - QTest::newRow("Close frame with close code POLICY VIOLATED") << QString(1, 'a') << QWebSocketProtocol::CC_POLICY_VIOLATED; - QTest::newRow("Close frame with close code PROTOCOL ERROR") << QString(1, 'a') << QWebSocketProtocol::CC_PROTOCOL_ERROR; - QTest::newRow("Close frame with close code TOO MUCH DATA") << QString(1, 'a') << QWebSocketProtocol::CC_TOO_MUCH_DATA; - QTest::newRow("Close frame with close code WRONG DATATYPE") << QString(1, 'a') << QWebSocketProtocol::CC_WRONG_DATATYPE; - 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::CC_NORMAL; - QTest::newRow("Close frame with close code BAD OPERATION and no reason") << QString() << QWebSocketProtocol::CC_BAD_OPERATION; - QTest::newRow("Close frame with close code DATATYPE NOT SUPPORTED and no reason") << QString() << QWebSocketProtocol::CC_DATATYPE_NOT_SUPPORTED; - QTest::newRow("Close frame with close code GOING AWAY and no reason") << QString() << QWebSocketProtocol::CC_GOING_AWAY; - QTest::newRow("Close frame with close code MISSING EXTENSION and no reason") << QString() << QWebSocketProtocol::CC_MISSING_EXTENSION; - QTest::newRow("Close frame with close code POLICY VIOLATED and no reason") << QString() << QWebSocketProtocol::CC_POLICY_VIOLATED; - QTest::newRow("Close frame with close code PROTOCOL ERROR and no reason") << QString() << QWebSocketProtocol::CC_PROTOCOL_ERROR; - QTest::newRow("Close frame with close code TOO MUCH DATA and no reason") << QString() << QWebSocketProtocol::CC_TOO_MUCH_DATA; - QTest::newRow("Close frame with close code WRONG DATATYPE and no reason") << QString() << QWebSocketProtocol::CC_WRONG_DATATYPE; - 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::goodBinaryFrame() { QByteArray data; @@ -314,23 +240,44 @@ void tst_DataProcessor::goodBinaryFrame() buffer.setData(data); buffer.open(QIODevice::ReadOnly); - QSignalSpy spyFrameReceived(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); - QSignalSpy spyMessageReceived(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); - QSignalSpy spyTextFrameReceived(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); - QSignalSpy spyTextMessageReceived(&dataProcessor, SIGNAL(textMessageReceived(QString))); + 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(spyFrameReceived.count(), 1); - QCOMPARE(spyMessageReceived.count(), 1); - QCOMPARE(spyTextFrameReceived.count(), 0); - QCOMPARE(spyTextMessageReceived.count(), 0); - QList<QVariant> arguments = spyFrameReceived.takeFirst(); + 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 = spyMessageReceived.takeFirst(); + arguments = binaryMessageReceivedSpy.takeFirst(); QCOMPARE(arguments.at(0).toByteArray().length(), payload.length()); buffer.close(); - spyFrameReceived.clear(); - spyMessageReceived.clear(); - data.clear(); +} + +void tst_DataProcessor::goodTextFrame_data() +{ + QTest::addColumn<QByteArray>("payload"); + + //test frames with small (< 126), large ( < 65536) and big ( > 65535) payloads + for (int i = 0; i < (65536 + 256); i += 128) + { + QTest::newRow(QString("Text frame with %1 ASCII characters").arg(i).toStdString().data()) << QByteArray(i, 'a'); + } + //test all valid ASCII characters + for (int i = 0; i < 128; ++i) + { + QTest::newRow(QString("Text frame with containing ASCII character '0x%1'").arg(QByteArray(1, char(i)).toHex().constData()).toStdString().data()) << QByteArray(1, char(i)); + } } void tst_DataProcessor::goodTextFrame() @@ -363,23 +310,124 @@ void tst_DataProcessor::goodTextFrame() buffer.setData(data); buffer.open(QIODevice::ReadOnly); - QSignalSpy spyFrameReceived(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); - QSignalSpy spyMessageReceived(&dataProcessor, SIGNAL(textMessageReceived(QString))); - QSignalSpy spyBinaryFrameReceived(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); - QSignalSpy spyBinaryMessageReceived(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); + 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(spyFrameReceived.count(), 1); - QCOMPARE(spyMessageReceived.count(), 1); - QCOMPARE(spyBinaryFrameReceived.count(), 0); - QCOMPARE(spyBinaryMessageReceived.count(), 0); - QList<QVariant> arguments = spyFrameReceived.takeFirst(); + + 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(), payload.length()); - arguments = spyMessageReceived.takeFirst(); + arguments = textMessageReceivedSpy.takeFirst(); QCOMPARE(arguments.at(0).toString().length(), payload.length()); buffer.close(); - spyFrameReceived.clear(); - spyMessageReceived.clear(); +} + +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::OC_PING)); + 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::OC_PONG)); + 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(QString("Close frame with %1 ASCII characters").arg(i).toStdString().data()) << QString(i, 'a') << QWebSocketProtocol::CC_NORMAL; + } + for (int i = 0; i < 126; ++i) + { + QTest::newRow(QString("Text frame with containing ASCII character '0x%1'").arg(QByteArray(1, char(i)).toHex().constData()).toStdString().data()) << QString(1, char(i)) << QWebSocketProtocol::CC_NORMAL; + } + QTest::newRow("Close frame with close code NORMAL") << QString(1, 'a') << QWebSocketProtocol::CC_NORMAL; + QTest::newRow("Close frame with close code BAD OPERATION") << QString(1, 'a') << QWebSocketProtocol::CC_BAD_OPERATION; + QTest::newRow("Close frame with close code DATATYPE NOT SUPPORTED") << QString(1, 'a') << QWebSocketProtocol::CC_DATATYPE_NOT_SUPPORTED; + QTest::newRow("Close frame with close code GOING AWAY") << QString(1, 'a') << QWebSocketProtocol::CC_GOING_AWAY; + QTest::newRow("Close frame with close code MISSING EXTENSION") << QString(1, 'a') << QWebSocketProtocol::CC_MISSING_EXTENSION; + QTest::newRow("Close frame with close code POLICY VIOLATED") << QString(1, 'a') << QWebSocketProtocol::CC_POLICY_VIOLATED; + QTest::newRow("Close frame with close code PROTOCOL ERROR") << QString(1, 'a') << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Close frame with close code TOO MUCH DATA") << QString(1, 'a') << QWebSocketProtocol::CC_TOO_MUCH_DATA; + QTest::newRow("Close frame with close code WRONG DATATYPE") << QString(1, 'a') << QWebSocketProtocol::CC_WRONG_DATATYPE; + 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::CC_NORMAL; + QTest::newRow("Close frame with close code BAD OPERATION and no reason") << QString() << QWebSocketProtocol::CC_BAD_OPERATION; + QTest::newRow("Close frame with close code DATATYPE NOT SUPPORTED and no reason") << QString() << QWebSocketProtocol::CC_DATATYPE_NOT_SUPPORTED; + QTest::newRow("Close frame with close code GOING AWAY and no reason") << QString() << QWebSocketProtocol::CC_GOING_AWAY; + QTest::newRow("Close frame with close code MISSING EXTENSION and no reason") << QString() << QWebSocketProtocol::CC_MISSING_EXTENSION; + QTest::newRow("Close frame with close code POLICY VIOLATED and no reason") << QString() << QWebSocketProtocol::CC_POLICY_VIOLATED; + QTest::newRow("Close frame with close code PROTOCOL ERROR and no reason") << QString() << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Close frame with close code TOO MUCH DATA and no reason") << QString() << QWebSocketProtocol::CC_TOO_MUCH_DATA; + QTest::newRow("Close frame with close code WRONG DATATYPE and no reason") << QString() << QWebSocketProtocol::CC_WRONG_DATATYPE; + 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::goodCloseFrame() @@ -406,24 +454,72 @@ void tst_DataProcessor::goodCloseFrame() buffer.setData(data); buffer.open(QIODevice::ReadOnly); - QSignalSpy spyCloseFrameReceived(&dataProcessor, SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); - QSignalSpy spyTextFrameReceived(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); - QSignalSpy spyTextMessageReceived(&dataProcessor, SIGNAL(textMessageReceived(QString))); - QSignalSpy spyBinaryFrameReceived(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); - QSignalSpy spyBinaryMessageReceived(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); + 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(spyCloseFrameReceived.count(), 1); - QCOMPARE(spyTextFrameReceived.count(), 0); - QCOMPARE(spyTextMessageReceived.count(), 0); - QCOMPARE(spyBinaryFrameReceived.count(), 0); - QCOMPARE(spyBinaryMessageReceived.count(), 0); - QList<QVariant> arguments = spyCloseFrameReceived.takeFirst(); + + 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(); - spyCloseFrameReceived.clear(); - data.clear(); +} + +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() @@ -438,8 +534,12 @@ void tst_DataProcessor::nonCharacterCodes() QByteArray data; QBuffer buffer; QWebSocketDataProcessor dataProcessor; - QSignalSpy frameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); - QSignalSpy messageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString))); + 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))); @@ -448,21 +548,22 @@ void tst_DataProcessor::nonCharacterCodes() buffer.setData(data); buffer.open(QIODevice::ReadOnly); dataProcessor.process(&buffer); - //QEXPECT_FAIL(QTest::currentDataTag(), "Due to QTextCode interpeting non-characters unicode points as invalid (QTBUG-33229).", Abort); - QCOMPARE(frameSpy.count(), 1); - QCOMPARE(messageSpy.count(), 1); + 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 = frameSpy.takeFirst(); + QVariantList arguments = textFrameSpy.takeFirst(); QCOMPARE(arguments.at(0).value<QString>().toUtf8(), payload); - arguments = messageSpy.takeFirst(); + QCOMPARE(arguments.at(1).value<bool>(), !isContinuationFrame); + arguments = textMessageSpy.takeFirst(); QCOMPARE(arguments.at(0).value<QString>().toUtf8(), payload); buffer.close(); - frameSpy.clear(); - messageSpy.clear(); - data.clear(); } } @@ -479,20 +580,32 @@ void tst_DataProcessor::frameTooSmall() //meaning the socket will be closed buffer.setData(data); buffer.open(QIODevice::ReadOnly); - QSignalSpy spy(&dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); + 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(spy.count(), 1); + + 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 = spy.takeFirst(); + + QList<QVariant> arguments = errorSpy.takeFirst(); QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), QWebSocketProtocol::CC_GOING_AWAY); - spy.clear(); + errorSpy.clear(); + closeSpy.clear(); + pingMessageSpy.clear(); + pongMessageSpy.clear(); textMessageSpy.clear(); binaryMessageSpy.clear(); textFrameSpy.clear(); @@ -505,16 +618,25 @@ void tst_DataProcessor::frameTooSmall() 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(spy.count(), 1); + + 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 = spy.takeFirst(); + + arguments = errorSpy.takeFirst(); QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), QWebSocketProtocol::CC_GOING_AWAY); buffer.close(); - spy.clear(); + errorSpy.clear(); + closeSpy.clear(); + pingMessageSpy.clear(); + pongMessageSpy.clear(); textMessageSpy.clear(); binaryMessageSpy.clear(); textFrameSpy.clear(); @@ -527,6 +649,7 @@ void tst_DataProcessor::frameTooSmall() data.append((char)(QWebSocketProtocol::OC_TEXT)).append(char(0x0)); buffer.setData(data); buffer.open(QIODevice::ReadOnly); + dataProcessor.process(&buffer); buffer.close(); @@ -536,16 +659,24 @@ void tst_DataProcessor::frameTooSmall() //meaning the socket will be closed buffer.setData(data); buffer.open(QIODevice::ReadOnly); - QSignalSpy spy(&dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); + QSignalSpy errorSpy(&dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); dataProcessor.process(&buffer); - QCOMPARE(spy.count(), 1); + + 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 = spy.takeFirst(); + + QList<QVariant> arguments = errorSpy.takeFirst(); QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), QWebSocketProtocol::CC_GOING_AWAY); - spy.clear(); + errorSpy.clear(); + closeSpy.clear(); + pingMessageSpy.clear(); + pongMessageSpy.clear(); textMessageSpy.clear(); binaryMessageSpy.clear(); textFrameSpy.clear(); @@ -559,6 +690,10 @@ void tst_DataProcessor::frameTooSmall() 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); @@ -566,7 +701,11 @@ void tst_DataProcessor::frameTooSmall() buffer.close(); data.clear(); - spy.clear(); + + errorSpy.clear(); + closeSpy.clear(); + pingMessageSpy.clear(); + pongMessageSpy.clear(); textMessageSpy.clear(); binaryMessageSpy.clear(); textFrameSpy.clear(); @@ -578,48 +717,421 @@ void tst_DataProcessor::frameTooSmall() buffer.open(QIODevice::ReadOnly); dataProcessor.process(&buffer); - QCOMPARE(spy.count(), 1); + 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 = spy.takeFirst(); + arguments = errorSpy.takeFirst(); QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), QWebSocketProtocol::CC_GOING_AWAY); - spy.clear(); 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::OC_TEXT) + << quint8(127) + << QByteArray(wireRepresentation, 8).append(QByteArray(32, 'a')) + << false + << QWebSocketProtocol::CC_TOO_MUCH_DATA; + + 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::OC_BINARY) + << quint8(127) + << QByteArray(wireRepresentation, 8).append(QByteArray(32, 'a')) + << false + << QWebSocketProtocol::CC_TOO_MUCH_DATA; + + 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::OC_CONTINUE) + << quint8(127) + << QByteArray(wireRepresentation, 8).append(QByteArray(32, 'a')) + << true + << QWebSocketProtocol::CC_TOO_MUCH_DATA; +} + 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"); + QTest::addColumn<QByteArray>("payload"); //superfluous, but present to be able to call doTest(), which expects a payload field + 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::OC_RESERVED_3); + invalidField("Invalid OpCode 4", QWebSocketProtocol::OC_RESERVED_4); + invalidField("Invalid OpCode 5", QWebSocketProtocol::OC_RESERVED_5); + invalidField("Invalid OpCode 6", QWebSocketProtocol::OC_RESERVED_6); + invalidField("Invalid OpCode 7", QWebSocketProtocol::OC_RESERVED_7); + invalidField("Invalid OpCode B", QWebSocketProtocol::OC_RESERVED_B); + invalidField("Invalid OpCode C", QWebSocketProtocol::OC_RESERVED_C); + invalidField("Invalid OpCode D", QWebSocketProtocol::OC_RESERVED_D); + invalidField("Invalid OpCode E", QWebSocketProtocol::OC_RESERVED_E); + invalidField("Invalid OpCode F", QWebSocketProtocol::OC_RESERVED_F); +} + 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::OC_CLOSE) << quint8(126) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Ping control frame with payload size 126") + << quint8(FIN | QWebSocketProtocol::OC_PING) << quint8(126) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Close control frame with payload size 126") + << quint8(FIN | QWebSocketProtocol::OC_PONG) << quint8(126) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; + + QTest::newRow("Non-final close control frame (fragmented)") + << quint8(QWebSocketProtocol::OC_CLOSE) << quint8(32) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Non-final ping control frame (fragmented)") + << quint8(QWebSocketProtocol::OC_PING) << quint8(32) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Non-final pong control frame (fragmented)") + << quint8(QWebSocketProtocol::OC_PONG) << quint8(32) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; +} + 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::OC_CLOSE) << quint8(1) << QByteArray(1, 'a') << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; + quint16 swapped = qToBigEndian<quint16>(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION); + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + swapped = qToBigEndian<quint16>(QWebSocketProtocol::CC_MISSING_STATUS_CODE); + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + swapped = qToBigEndian<quint16>(QWebSocketProtocol::CC_TLS_HANDSHAKE_FAILED); + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; + 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::OC_CLOSE) << + quint8(2) << + QByteArray(wireRepresentation, 2) << + false << + QWebSocketProtocol::CC_PROTOCOL_ERROR; +} + 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", "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbe", isControlFrame); + + //6.13: lonely start characters + invalidUTF8("case 6.13.1", "c020c120c220c320c420c520c620c720c820c920ca20cb20cc20cd20ce20cf20d020d120d220d320d420d520d620d720d820d920da20db20dc20dd20de20", 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); @@ -633,7 +1145,10 @@ void tst_DataProcessor::invalidPayloadInCloseFrame() QByteArray data; QBuffer buffer; QWebSocketDataProcessor dataProcessor; - QSignalSpy spy(&dataProcessor, SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); + 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))); @@ -644,16 +1159,43 @@ void tst_DataProcessor::invalidPayloadInCloseFrame() buffer.setData(data); buffer.open(QIODevice::ReadOnly); dataProcessor.process(&buffer); - QCOMPARE(spy.count(), 1); + 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 = spy.takeFirst(); + QVariantList arguments = closeSpy.takeFirst(); QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode); buffer.close(); - spy.clear(); - data.clear(); +} + +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::OC_TEXT, 125, 0); + incompleteFrame(QWebSocketProtocol::OC_TEXT, 64, 32); + incompleteFrame(QWebSocketProtocol::OC_TEXT, 256, 32); + incompleteFrame(QWebSocketProtocol::OC_TEXT, 128000, 32); + incompleteFrame(QWebSocketProtocol::OC_BINARY, 125, 0); + incompleteFrame(QWebSocketProtocol::OC_BINARY, 64, 32); + incompleteFrame(QWebSocketProtocol::OC_BINARY, 256, 32); + incompleteFrame(QWebSocketProtocol::OC_BINARY, 128000, 32); + incompleteFrame(QWebSocketProtocol::OC_CONTINUE, 125, 0); + incompleteFrame(QWebSocketProtocol::OC_CONTINUE, 64, 32); + incompleteFrame(QWebSocketProtocol::OC_CONTINUE, 256, 32); + incompleteFrame(QWebSocketProtocol::OC_CONTINUE, 128000, 32); + + incompleteFrame(QWebSocketProtocol::OC_CLOSE, 64, 32); + incompleteFrame(QWebSocketProtocol::OC_PING, 64, 32); + incompleteFrame(QWebSocketProtocol::OC_PONG, 64, 32); } void tst_DataProcessor::incompletePayload() @@ -661,6 +1203,33 @@ 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(); @@ -680,7 +1249,7 @@ void tst_DataProcessor::doTest() QByteArray data; QBuffer buffer; QWebSocketDataProcessor dataProcessor; - QSignalSpy spy(&dataProcessor, SIGNAL(errorEncountered(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))); @@ -695,15 +1264,15 @@ void tst_DataProcessor::doTest() buffer.setData(data); buffer.open(QIODevice::ReadOnly); dataProcessor.process(&buffer); - QCOMPARE(spy.count(), 1); + QCOMPARE(errorSpy.count(), 1); QCOMPARE(textMessageSpy.count(), 0); QCOMPARE(binaryMessageSpy.count(), 0); QCOMPARE(textFrameSpy.count(), isContinuationFrame ? 1 : 0); QCOMPARE(binaryFrameSpy.count(), 0); - QVariantList arguments = spy.takeFirst(); + QVariantList arguments = errorSpy.takeFirst(); QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode); buffer.close(); - spy.clear(); + errorSpy.clear(); data.clear(); } @@ -720,7 +1289,7 @@ void tst_DataProcessor::doCloseFrameTest() QByteArray data; QBuffer buffer; QWebSocketDataProcessor dataProcessor; - QSignalSpy spy(&dataProcessor, SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); + 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))); @@ -732,17 +1301,15 @@ void tst_DataProcessor::doCloseFrameTest() buffer.setData(data); buffer.open(QIODevice::ReadOnly); dataProcessor.process(&buffer); - QCOMPARE(spy.count(), 1); + 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 = spy.takeFirst(); + QVariantList arguments = closeSpy.takeFirst(); QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode); buffer.close(); - spy.clear(); - data.clear(); } QString tst_DataProcessor::opCodeToString(quint8 opCode) @@ -1021,476 +1588,6 @@ void tst_DataProcessor::insertIncompleteSizeFieldTest(quint8 payloadCode, quint8 << QWebSocketProtocol::CC_GOING_AWAY; } -////////////////////////////////////////////////////////////////////////////////// -//// TEST DATA -////////////////////////////////////////////////////////////////////////////////// -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"); - QTest::addColumn<QByteArray>("payload"); //superfluous, but present to be able to call doTest(), which expects a payload field - 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::OC_RESERVED_3); - invalidField("Invalid OpCode 4", QWebSocketProtocol::OC_RESERVED_4); - invalidField("Invalid OpCode 5", QWebSocketProtocol::OC_RESERVED_5); - invalidField("Invalid OpCode 6", QWebSocketProtocol::OC_RESERVED_6); - invalidField("Invalid OpCode 7", QWebSocketProtocol::OC_RESERVED_7); - invalidField("Invalid OpCode B", QWebSocketProtocol::OC_RESERVED_B); - invalidField("Invalid OpCode C", QWebSocketProtocol::OC_RESERVED_C); - invalidField("Invalid OpCode D", QWebSocketProtocol::OC_RESERVED_D); - invalidField("Invalid OpCode E", QWebSocketProtocol::OC_RESERVED_E); - invalidField("Invalid OpCode F", QWebSocketProtocol::OC_RESERVED_F); -} - -void tst_DataProcessor::invalidPayloadInCloseFrame_data() -{ - invalidPayload_data(true); -} - -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", "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbe", isControlFrame); - - //6.13: lonely start characters - invalidUTF8("case 6.13.1", "c020c120c220c320c420c520c620c720c820c920ca20cb20cc20cd20ce20cf20d020d120d220d320d420d520d620d720d820d920da20db20dc20dd20de20", 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::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::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::OC_CLOSE) << quint8(126) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; - QTest::newRow("Ping control frame with payload size 126") - << quint8(FIN | QWebSocketProtocol::OC_PING) << quint8(126) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; - QTest::newRow("Close control frame with payload size 126") - << quint8(FIN | QWebSocketProtocol::OC_PONG) << quint8(126) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; - - QTest::newRow("Non-final close control frame (fragmented)") - << quint8(QWebSocketProtocol::OC_CLOSE) << quint8(32) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; - QTest::newRow("Non-final ping control frame (fragmented)") - << quint8(QWebSocketProtocol::OC_PING) << quint8(32) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; - QTest::newRow("Non-final pong control frame (fragmented)") - << quint8(QWebSocketProtocol::OC_PONG) << quint8(32) << QByteArray() << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; -} - -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::OC_CLOSE) << quint8(1) << QByteArray(1, 'a') << false << QWebSocketProtocol::CC_PROTOCOL_ERROR; - quint16 swapped = qToBigEndian<quint16>(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION); - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - swapped = qToBigEndian<quint16>(QWebSocketProtocol::CC_MISSING_STATUS_CODE); - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - swapped = qToBigEndian<quint16>(QWebSocketProtocol::CC_TLS_HANDSHAKE_FAILED); - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; - 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::OC_CLOSE) << - quint8(2) << - QByteArray(wireRepresentation, 2) << - false << - QWebSocketProtocol::CC_PROTOCOL_ERROR; -} - -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::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::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::OC_TEXT, 125, 0); - incompleteFrame(QWebSocketProtocol::OC_TEXT, 64, 32); - incompleteFrame(QWebSocketProtocol::OC_TEXT, 256, 32); - incompleteFrame(QWebSocketProtocol::OC_TEXT, 128000, 32); - incompleteFrame(QWebSocketProtocol::OC_BINARY, 125, 0); - incompleteFrame(QWebSocketProtocol::OC_BINARY, 64, 32); - incompleteFrame(QWebSocketProtocol::OC_BINARY, 256, 32); - incompleteFrame(QWebSocketProtocol::OC_BINARY, 128000, 32); - incompleteFrame(QWebSocketProtocol::OC_CONTINUE, 125, 0); - incompleteFrame(QWebSocketProtocol::OC_CONTINUE, 64, 32); - incompleteFrame(QWebSocketProtocol::OC_CONTINUE, 256, 32); - incompleteFrame(QWebSocketProtocol::OC_CONTINUE, 128000, 32); - - incompleteFrame(QWebSocketProtocol::OC_CLOSE, 64, 32); - incompleteFrame(QWebSocketProtocol::OC_PING, 64, 32); - incompleteFrame(QWebSocketProtocol::OC_PONG, 64, 32); -} - -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::OC_TEXT) - << quint8(127) - << QByteArray(wireRepresentation, 8).append(QByteArray(32, 'a')) - << false - << QWebSocketProtocol::CC_TOO_MUCH_DATA; - - 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::OC_BINARY) - << quint8(127) - << QByteArray(wireRepresentation, 8).append(QByteArray(32, 'a')) - << false - << QWebSocketProtocol::CC_TOO_MUCH_DATA; - - 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::OC_CONTINUE) - << quint8(127) - << QByteArray(wireRepresentation, 8).append(QByteArray(32, 'a')) - << true - << QWebSocketProtocol::CC_TOO_MUCH_DATA; -} - QTEST_MAIN(tst_DataProcessor) #include "tst_dataprocessor.moc" |