Added tests for valid and invalid close codes
authorKurt Pattyn <pattyn.kurt@gmail.com>
Sun, 1 Sep 2013 11:53:26 +0000 (13:53 +0200)
committerKurt Pattyn <pattyn.kurt@gmail.com>
Sun, 1 Sep 2013 11:53:26 +0000 (13:53 +0200)
Added tests for invalid UTF8 sequences in close frames

tests/tst_dataprocessor.cpp

index 3f89dc3..89685c9 100644 (file)
@@ -15,11 +15,14 @@ Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
 //TODO: test on masking correctness
 //TODO: test for valid fields
 //TODO: test for valid opcodes
+//DONE: test for valid close codes
+//TODO: test close frame with no close code and reason
 
 //TODO: test valid frame sequences
 
 //unhappy flow
 //DONE: test invalid UTF8 sequences
+//DONE: test invalid UTF8 sequences in control/close frames
 //TODO: test invalid masks
 //DONE: test for invalid fields
 //DONE: test for invalid opcodes
@@ -29,12 +32,9 @@ Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
 //DONE: test continuation frames for incomplete payload
 
 //TODO: besides spying on errors, we should also check if the frame and message signals are not emitted (or partially emitted)
+//DONE: test close frame with payload length 1 (is either 0, if no close code, or at least 2, close code + optional reason)
 
 //TODO: test invalid frame sequences
-/*
-Control frames are only checked in qwebsocket_p! These checks should probably be moved to
-/TODO: test invalid UTF8 sequences in control/close frames
-*/
 
 const quint8 FIN = 0x80;
 const quint8 RSV1 = 0x40;
@@ -106,6 +106,7 @@ private Q_SLOTS:
         This test does not test sequences of frames, only single frames are tested
      */
     void invalidControlFrame();
+    void invalidCloseFrame();
 
     /*!
         \brief Tests the DataProcess for the correct handling of incomplete size fields for large and big payloads.
@@ -129,6 +130,8 @@ private Q_SLOTS:
      */
     void invalidPayload();
 
+    void invalidPayloadInCloseFrame();
+
     /*!
       Tests the DataProcessor for the correct handling of the minimum size representation requirement of RFC 6455 (see paragraph 5.2)
      */
@@ -137,9 +140,11 @@ private Q_SLOTS:
     void invalidHeader_data();
     void incompleteSizeField_data();
     void incompletePayload_data();
-    void invalidPayload_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();
@@ -149,7 +154,7 @@ private Q_SLOTS:
 
 private:
     //helper function that constructs a new row of test data for invalid UTF8 sequences
-    void invalidUTF8(const char *dataTag, const char *utf8Sequence);
+    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
@@ -163,6 +168,7 @@ private:
     void nonCharacterSequence(const char *sequence);
 
     void doTest();
+    void doCloseFrameTest();
 
     QString opCodeToString(quint8 opCode);
 };
@@ -230,6 +236,24 @@ void tst_DataProcessor::goodControlFrames_data()
     {
         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);
+
+    //Not allowed per RFC 6455 (see para 7.4.1)
+    //QTest::newRow("Close frame with close code ABNORMAL DISCONNECTION") << QString(1, 'a') << QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION;
+    //QTest::newRow("Close frame with close code MISSING STATUS CODE") << QString(1, 'a') << QWebSocketProtocol::CC_MISSING_STATUS_CODE;
+    //QTest::newRow("Close frame with close code HANDSHAKE FAILED") << QString(1, 'a') << QWebSocketProtocol::CC_TLS_HANDSHAKE_FAILED;
 }
 
 void tst_DataProcessor::goodBinaryFrames()
@@ -478,6 +502,11 @@ void tst_DataProcessor::invalidControlFrame()
     doTest();
 }
 
+void tst_DataProcessor::invalidCloseFrame()
+{
+    doCloseFrameTest();
+}
+
 void tst_DataProcessor::minimumSizeRequirement()
 {
     doTest();
@@ -488,6 +517,42 @@ void tst_DataProcessor::invalidPayload()
     doTest();
 }
 
+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;
+    DataProcessor dataProcessor;
+    QSignalSpy spy(&dataProcessor, SIGNAL(closeReceived(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::ReadWrite);
+    dataProcessor.process(&buffer);
+    QCOMPARE(spy.count(), 1);
+    QCOMPARE(textMessageSpy.count(), 0);
+    QCOMPARE(binaryMessageSpy.count(), 0);
+    QCOMPARE(textFrameSpy.count(), 0);
+    QCOMPARE(binaryFrameSpy.count(), 0);
+    QVariantList arguments = spy.takeFirst();
+    QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode);
+    buffer.close();
+    spy.clear();
+    data.clear();
+}
+
 void tst_DataProcessor::incompletePayload()
 {
     doTest();
@@ -524,6 +589,35 @@ void tst_DataProcessor::doTest()
     buffer.open(QIODevice::ReadWrite);
     dataProcessor.process(&buffer);
     QCOMPARE(spy.count(), 1);
+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;
+    DataProcessor dataProcessor;
+    QSignalSpy spy(&dataProcessor, SIGNAL(closeReceived(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::ReadWrite);
+    dataProcessor.process(&buffer);
+    QCOMPARE(spy.count(), 1);
+    QCOMPARE(textMessageSpy.count(), 0);
+    QCOMPARE(binaryMessageSpy.count(), 0);
+    QCOMPARE(textFrameSpy.count(), 0);
+    QCOMPARE(binaryFrameSpy.count(), 0);
     QVariantList arguments = spy.takeFirst();
     QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode);
     buffer.close();
@@ -676,9 +770,23 @@ void tst_DataProcessor::minimumSize64Bit(quint64 sizeInBytes)
             << QWebSocketProtocol::CC_PROTOCOL_ERROR;
 }
 
-void tst_DataProcessor::invalidUTF8(const char *dataTag, const char *utf8Sequence)
+void tst_DataProcessor::invalidUTF8(const char *dataTag, const char *utf8Sequence, bool isCloseFrame)
 {
     QByteArray payload = QByteArray::fromHex(utf8Sequence);
+
+    if (isCloseFrame)
+    {
+        quint16 closeCode = qToBigEndian<quint16>(QWebSocketProtocol::CC_NORMAL);
+        const char *wireRepresentation = static_cast<const char *>(static_cast<const void *>(&closeCode));
+        QTest::newRow(QString("Close frame with invalid UTF8-sequence: %1").arg(dataTag).toStdString().data())
+                << quint8(FIN | QWebSocketProtocol::OC_CLOSE)
+                << quint8(payload.length() + 2)
+                << QByteArray(wireRepresentation, 2).append(payload)
+                << false
+                << QWebSocketProtocol::CC_WRONG_DATATYPE;
+    }
+    else
+    {
     QTest::newRow(QString("Text frame with invalid UTF8-sequence: %1").arg(dataTag).toStdString().data())
             << quint8(FIN | QWebSocketProtocol::OC_TEXT)
             << quint8(payload.length())
@@ -693,6 +801,7 @@ void tst_DataProcessor::invalidUTF8(const char *dataTag, const char *utf8Sequenc
             << true
             << QWebSocketProtocol::CC_WRONG_DATATYPE;
 }
+}
 
 void tst_DataProcessor::invalidField(const char *dataTag, quint8 invalidFieldValue)
 {
@@ -717,11 +826,6 @@ void tst_DataProcessor::incompleteFrame(quint8 controlCode, quint64 indicatedSiz
     QString frameType = opCodeToString(controlCode);
     QByteArray firstFrame;
 
-//    if (controlCode == QWebSocketProtocol::OC_CONTINUE)
-//    {
-//        firstFrame.append(quint8(QWebSocketProtocol::OC_TEXT)).append(char(0)).append(QByteArray());
-//    }
-
     if (indicatedSize < 126)
     {
         QTest::newRow(frameType.append(QString(" frame with payload size %1, but only %2 bytes of data").arg(indicatedSize).arg(actualPayloadSize)).toStdString().data())
@@ -830,11 +934,14 @@ void tst_DataProcessor::invalidHeader_data()
     invalidField("Invalid OpCode D", QWebSocketProtocol::OC_RESERVED_D);
     invalidField("Invalid OpCode E", QWebSocketProtocol::OC_RESERVED_E);
     invalidField("Invalid OpCode F", QWebSocketProtocol::OC_RESERVED_F);
+}
 
-    //invalidField("Continuation Frame without previous data frame", QWebSocketProtocol::OC_CONTINUE);
+void tst_DataProcessor::invalidPayloadInCloseFrame_data()
+{
+    invalidPayload_data(true);
 }
 
-void tst_DataProcessor::invalidPayload_data()
+void tst_DataProcessor::invalidPayload_data(bool isControlFrame)
 {
     QTest::addColumn<quint8>("firstByte");
     QTest::addColumn<quint8>("secondByte");
@@ -843,108 +950,108 @@ void tst_DataProcessor::invalidPayload_data()
     QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedCloseCode");
 
     //6.3: invalid UTF-8 sequence
-    invalidUTF8("case 6.3.1",   "cebae1bdb9cf83cebcceb5eda080656469746564");
+    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");
-    invalidUTF8("case 6.4.4",   "cebae1bdb9cf83cebcceb5eda080656469746564");
+    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");
-    invalidUTF8("case 6.6.3",   "cebae1");
-    invalidUTF8("case 6.6.4",   "cebae1bd");
-    invalidUTF8("case 6.6.6",   "cebae1bdb9cf");
-    invalidUTF8("case 6.6.8",   "cebae1bdb9cf83ce");
-    invalidUTF8("case 6.6.10",  "cebae1bdb9cf83cebcce");
+    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");
-    invalidUTF8("case 6.8.2",   "fc8480808080");
+    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");
-    invalidUTF8("case 6.10.2",  "fbbfbfbfbf");
-    invalidUTF8("case 6.10.3",  "fdbfbfbfbfbf");
+    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");
+    invalidUTF8("case 6.11.5",  "f4908080", isControlFrame);
 
     //6.12: unexpected continuation bytes
-    invalidUTF8("case 6.12.1",  "80");
-    invalidUTF8("case 6.12.2",  "bf");
-    invalidUTF8("case 6.12.3",  "80bf");
-    invalidUTF8("case 6.12.4",  "80bf80");
-    invalidUTF8("case 6.12.5",  "80bf80bf");
-    invalidUTF8("case 6.12.6",  "80bf80bf80");
-    invalidUTF8("case 6.12.7",  "80bf80bf80bf");
-    invalidUTF8("case 6.12.8",  "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbe");
+    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");
-    invalidUTF8("case 6.13.2",  "e020e120e220e320e420e520e620e720e820e920ea20eb20ec20ed20ee20");
-    invalidUTF8("case 6.13.3",  "f020f120f220f320f420f520f620");
-    invalidUTF8("case 6.13.4",  "f820f920fa20");
-    invalidUTF8("case 6.13.5",  "fc20");
+    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");
-    invalidUTF8("case 6.14.2",  "e080");
-    invalidUTF8("case 6.14.3",  "f08080");
-    invalidUTF8("case 6.14.4",  "f8808080");
-    invalidUTF8("case 6.14.5",  "fc80808080");
-    invalidUTF8("case 6.14.6",  "df");
-    invalidUTF8("case 6.14.7",  "efbf");
-    invalidUTF8("case 6.14.8",  "f7bfbf");
-    invalidUTF8("case 6.14.9",  "fbbfbfbf");
-    invalidUTF8("case 6.14.10", "fdbfbfbfbf");
+    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");
+    invalidUTF8("case 6.15.1",  "c0e080f08080f8808080fc80808080dfefbff7bfbffbbfbfbffdbfbfbfbf", isControlFrame);
 
     //6.16: impossible bytes
-    invalidUTF8("case 6.16.1",  "fe");
-    invalidUTF8("case 6.16.2",  "ff");
-    invalidUTF8("case 6.16.3",  "fefeffff");
+    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");
-    invalidUTF8("case 6.17.2",  "e080af");
-    invalidUTF8("case 6.17.3",  "f08080af");
-    invalidUTF8("case 6.17.4",  "f8808080af");
-    invalidUTF8("case 6.17.5",  "fc80808080af");
+    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");
-    invalidUTF8("case 6.18.2",  "e09fbf");
-    invalidUTF8("case 6.18.3",  "f08fbfbf");
-    invalidUTF8("case 6.18.4",  "f887bfbfbf");
-    invalidUTF8("case 6.18.5",  "fc83bfbfbfbf");
+    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");
-    invalidUTF8("case 6.19.2",  "e08080");
-    invalidUTF8("case 6.19.3",  "f0808080");
-    invalidUTF8("case 6.19.4",  "f880808080");
-    invalidUTF8("case 6.19.5",  "fc8080808080");
+    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");
-    invalidUTF8("case 6.20.2",  "edadbf");
-    invalidUTF8("case 6.20.3",  "edae80");
-    invalidUTF8("case 6.20.4",  "edafbf");
-    invalidUTF8("case 6.20.5",  "edb080");
-    invalidUTF8("case 6.20.6",  "edbe80");
-    invalidUTF8("case 6.20.7",  "edbfbf");
+    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");
-    invalidUTF8("case 6.21.2",  "eda080edbfbf");
-    invalidUTF8("case 6.21.3",  "edadbfedb080");
-    invalidUTF8("case 6.21.4",  "edadbfedbfbf");
-    invalidUTF8("case 6.21.5",  "edae80edb080");
-    invalidUTF8("case 6.21.6",  "edae80edbfbf");
-    invalidUTF8("case 6.21.7",  "edafbfedb080");
-    invalidUTF8("case 6.21.8",  "edafbfedbfbf");
+    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()
@@ -982,6 +1089,7 @@ void tst_DataProcessor::invalidControlFrame_data()
     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")
@@ -997,6 +1105,96 @@ void tst_DataProcessor::invalidControlFrame_data()
             << 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;
+    QTest::newRow("Close control frame close code ABNORMAL DISCONNECTION")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION;
+    QTest::newRow("Close control frame close code MISSING STATUS CODE")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CC_MISSING_STATUS_CODE;
+    QTest::newRow("Close control frame close code 1004")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CC_RESERVED_1004;
+    QTest::newRow("Close control frame close code TLS HANDSHAKE FAILED")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CC_TLS_HANDSHAKE_FAILED;
+    QTest::newRow("Close control frame close code 0")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CloseCode(0);
+    QTest::newRow("Close control frame close code 999")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CloseCode(999);
+    QTest::newRow("Close control frame close code 1012")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CloseCode(1012);
+    QTest::newRow("Close control frame close code 1013")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CloseCode(1013);
+    QTest::newRow("Close control frame close code 1014")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CloseCode(1014);
+    QTest::newRow("Close control frame close code 1014")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CloseCode(1016);
+    QTest::newRow("Close control frame close code 1100")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CloseCode(1100);
+    QTest::newRow("Close control frame close code 2000")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CloseCode(2000);
+    QTest::newRow("Close control frame close code 2999")
+            << quint8(FIN | QWebSocketProtocol::OC_CLOSE) <<
+               quint8(2) <<
+               QByteArray() <<
+               false <<
+               QWebSocketProtocol::CloseCode(2999);
+}
+
 void tst_DataProcessor::incompleteSizeField_data()
 {
     QTest::addColumn<quint8>("firstByte");