| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the test suite of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| 21 | ** included in the packaging of this file. Please review the following |
| 22 | ** information to ensure the GNU General Public License requirements will |
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| 24 | ** |
| 25 | ** $QT_END_LICENSE$ |
| 26 | ** |
| 27 | ****************************************************************************/ |
| 28 | #include <QtTest/QtTest> |
| 29 | #include <QtTest/qtestcase.h> |
| 30 | #include <QtTest/QSignalSpy> |
| 31 | #include <QtCore/QBuffer> |
| 32 | #include <QtCore/QByteArray> |
| 33 | #include <QtCore/QDebug> |
| 34 | |
| 35 | #include "private/qwebsocketdataprocessor_p.h" |
| 36 | #include "private/qwebsocketprotocol_p.h" |
| 37 | #include "QtWebSockets/qwebsocketprotocol.h" |
| 38 | |
| 39 | const quint8 FIN = 0x80; |
| 40 | const quint8 RSV1 = 0x40; |
| 41 | const quint8 RSV2 = 0x30; |
| 42 | const quint8 RSV3 = 0x10; |
| 43 | |
| 44 | QT_USE_NAMESPACE |
| 45 | |
| 46 | Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode) |
| 47 | Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode) |
| 48 | |
| 49 | class tst_DataProcessor : public QObject |
| 50 | { |
| 51 | Q_OBJECT |
| 52 | |
| 53 | public: |
| 54 | tst_DataProcessor(); |
| 55 | |
| 56 | private Q_SLOTS: |
| 57 | void initTestCase(); |
| 58 | void cleanupTestCase(); |
| 59 | void init(); |
| 60 | void cleanup(); |
| 61 | |
| 62 | /*************************************************************************** |
| 63 | * Happy Flows |
| 64 | ***************************************************************************/ |
| 65 | /*! |
| 66 | Tests all kinds of valid binary frames, including zero length frames |
| 67 | */ |
| 68 | void goodBinaryFrame(); |
| 69 | void goodBinaryFrame_data(); |
| 70 | |
| 71 | /*! |
| 72 | Tests all kinds of valid text frames, including zero length frames |
| 73 | */ |
| 74 | void goodTextFrame(); |
| 75 | void goodTextFrame_data(); |
| 76 | |
| 77 | /*! |
| 78 | * Test all kinds of valid control frames. |
| 79 | */ |
| 80 | void goodControlFrame(); |
| 81 | |
| 82 | /*! |
| 83 | * Test all kinds of valid close frames. |
| 84 | */ |
| 85 | void goodCloseFrame(); |
| 86 | void goodCloseFrame_data(); |
| 87 | |
| 88 | /*! |
| 89 | * Test all valid opcodes |
| 90 | */ |
| 91 | void goodOpcodes(); |
| 92 | void goodOpcodes_data(); |
| 93 | |
| 94 | /*! |
| 95 | Tests the QWebSocketDataProcessor for the correct handling of non-charactercodes |
| 96 | Due to a workaround in QTextCodec, non-characters are treated as illegal. |
| 97 | This workaround is not necessary anymore, and hence code should be changed in Qt |
| 98 | to allow non-characters again. |
| 99 | */ |
| 100 | void nonCharacterCodes(); |
| 101 | void nonCharacterCodes_data(); |
| 102 | |
| 103 | /*************************************************************************** |
| 104 | * Rainy Day Flows |
| 105 | ***************************************************************************/ |
| 106 | /*! |
| 107 | Tests the QWebSocketDataProcessor for correct handling of frames that don't |
| 108 | contain the starting 2 bytes. |
| 109 | This test is a border case, where not enough bytes are received to even start parsing a |
| 110 | frame. |
| 111 | This test does not test sequences of frames, only single frames are tested |
| 112 | */ |
| 113 | void frameTooSmall(); |
| 114 | |
| 115 | /*! |
| 116 | Tests the QWebSocketDataProcessor for correct handling of frames that are oversized. |
| 117 | This test does not test sequences of frames, only single frames are tested |
| 118 | */ |
| 119 | void frameTooBig(); |
| 120 | void frameTooBig_data(); |
| 121 | |
| 122 | /*! |
| 123 | Tests the QWebSocketDataProcessor for the correct handling of malformed frame headers. |
| 124 | This test does not test sequences of frames, only single frames are tested |
| 125 | */ |
| 126 | void invalidHeader(); |
| 127 | void invalidHeader_data(); |
| 128 | |
| 129 | /*! |
| 130 | Tests the QWebSocketDataProcessor for the correct handling of invalid control frames. |
| 131 | Invalid means: payload bigger than 125, frame is fragmented, ... |
| 132 | This test does not test sequences of frames, only single frames are tested |
| 133 | */ |
| 134 | void invalidControlFrame(); |
| 135 | void invalidControlFrame_data(); |
| 136 | |
| 137 | void invalidCloseFrame(); |
| 138 | void invalidCloseFrame_data(); |
| 139 | |
| 140 | /*! |
| 141 | Tests the QWebSocketDataProcessor for the correct handling of incomplete size fields |
| 142 | for large and big payloads. |
| 143 | */ |
| 144 | void incompleteSizeField(); |
| 145 | void incompleteSizeField_data(); |
| 146 | |
| 147 | /*! |
| 148 | Tests the QWebSocketDataProcessor for the correct handling of incomplete payloads. |
| 149 | This includes: |
| 150 | - incomplete length bytes for large and big payloads (16- and 64-bit values), |
| 151 | - minimum size representation (see RFC 6455 paragraph 5.2), |
| 152 | - frames that are too large (larger than MAX_INT in bytes) |
| 153 | - incomplete payloads (less bytes than indicated in the size field) |
| 154 | This test does not test sequences of frames, only single frames are tested |
| 155 | */ |
| 156 | void incompletePayload(); |
| 157 | void incompletePayload_data(); |
| 158 | |
| 159 | /*! |
| 160 | Tests the QWebSocketDataProcessor for the correct handling of invalid UTF-8 payloads. |
| 161 | This test does not test sequences of frames, only single frames are tested |
| 162 | */ |
| 163 | void invalidPayload(); |
| 164 | void invalidPayload_data(bool isControlFrame = false); |
| 165 | |
| 166 | void invalidPayloadInCloseFrame(); |
| 167 | void invalidPayloadInCloseFrame_data(); |
| 168 | |
| 169 | /*! |
| 170 | Tests the QWebSocketDataProcessor for the correct handling of the minimum size representation |
| 171 | requirement of RFC 6455 (see paragraph 5.2) |
| 172 | */ |
| 173 | void minimumSizeRequirement(); |
| 174 | void minimumSizeRequirement_data(); |
| 175 | |
| 176 | void clearDataBuffers(); // qtbug-55506 |
| 177 | |
| 178 | private: |
| 179 | //helper function that constructs a new row of test data for invalid UTF8 sequences |
| 180 | void invalidUTF8(const char *dataTag, const char *utf8Sequence, bool isCloseFrame); |
| 181 | //helper function that constructs a new row of test data for invalid leading field values |
| 182 | void invalidField(const char *dataTag, quint8 invalidFieldValue); |
| 183 | //helper functions that construct a new row of test data for size fields that do not adhere |
| 184 | //to the minimum size requirement |
| 185 | void minimumSize16Bit(quint16 sizeInBytes); |
| 186 | void minimumSize64Bit(quint64 sizeInBytes); |
| 187 | //helper function to construct a new row of test data containing frames with a payload size |
| 188 | //smaller than indicated in the header |
| 189 | void incompleteFrame(quint8 controlCode, quint64 indicatedSize, quint64 actualPayloadSize); |
| 190 | void insertIncompleteSizeFieldTest(quint8 payloadCode, quint8 numBytesFollowing); |
| 191 | |
| 192 | //helper function to construct a new row of test data containing text frames containing |
| 193 | //sequences |
| 194 | void nonCharacterSequence(const char *sequence); |
| 195 | |
| 196 | void doTest(int timeout = 0); |
| 197 | void doCloseFrameTest(); |
| 198 | |
| 199 | QString opCodeToString(quint8 opCode); |
| 200 | }; |
| 201 | |
| 202 | tst_DataProcessor::tst_DataProcessor() |
| 203 | { |
| 204 | } |
| 205 | |
| 206 | void tst_DataProcessor::initTestCase() |
| 207 | { |
| 208 | } |
| 209 | |
| 210 | void tst_DataProcessor::cleanupTestCase() |
| 211 | { |
| 212 | } |
| 213 | |
| 214 | void tst_DataProcessor::init() |
| 215 | { |
| 216 | qRegisterMetaType<QWebSocketProtocol::OpCode>(typeName: "QWebSocketProtocol::OpCode" ); |
| 217 | qRegisterMetaType<QWebSocketProtocol::CloseCode>(typeName: "QWebSocketProtocol::CloseCode" ); |
| 218 | } |
| 219 | |
| 220 | void tst_DataProcessor::cleanup() |
| 221 | { |
| 222 | } |
| 223 | |
| 224 | void tst_DataProcessor::goodBinaryFrame_data() |
| 225 | { |
| 226 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 227 | //be sure to get small (< 126 bytes), large (> 125 bytes & < 64K) and big (>64K) frames |
| 228 | for (int i = 0; i < (65536 + 256); i += 128) |
| 229 | { |
| 230 | QTest::newRow(QStringLiteral("Binary frame with %1 bytes" ).arg(a: i).toLatin1().constData()) |
| 231 | << QByteArray(i, char(1)); |
| 232 | } |
| 233 | for (int i = 0; i < 256; ++i) //test all possible bytes in the payload |
| 234 | { |
| 235 | QTest::newRow(QStringLiteral("Binary frame containing byte: '0x%1'" ) |
| 236 | .arg(a: QByteArray(1, char(i)).toHex().constData()).toLatin1().constData()) |
| 237 | << QByteArray(i, char(1)); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | void tst_DataProcessor::goodBinaryFrame() |
| 242 | { |
| 243 | QByteArray data; |
| 244 | QBuffer buffer; |
| 245 | QWebSocketDataProcessor dataProcessor; |
| 246 | QFETCH(QByteArray, payload); |
| 247 | |
| 248 | data.append(c: char(FIN | QWebSocketProtocol::OpCodeBinary)); |
| 249 | |
| 250 | if (payload.length() < 126) |
| 251 | { |
| 252 | data.append(c: char(payload.length())); |
| 253 | } |
| 254 | else if (payload.length() < 65536) |
| 255 | { |
| 256 | quint16 swapped = qToBigEndian<quint16>(source: payload.length()); |
| 257 | const char *wireRepresentation |
| 258 | = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 259 | data.append(c: char(126)).append(s: wireRepresentation, len: 2); |
| 260 | } |
| 261 | else |
| 262 | { |
| 263 | quint64 swapped = qToBigEndian<quint64>(source: payload.length()); |
| 264 | const char *wireRepresentation |
| 265 | = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 266 | data.append(c: char(127)).append(s: wireRepresentation, len: 8); |
| 267 | } |
| 268 | |
| 269 | data.append(a: payload); |
| 270 | buffer.setData(data); |
| 271 | buffer.open(openMode: QIODevice::ReadOnly); |
| 272 | |
| 273 | QSignalSpy errorReceivedSpy(&dataProcessor, |
| 274 | SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); |
| 275 | QSignalSpy closeReceivedSpy(&dataProcessor, |
| 276 | SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); |
| 277 | QSignalSpy pingReceivedSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray))); |
| 278 | QSignalSpy pongReceivedSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray))); |
| 279 | QSignalSpy binaryFrameReceivedSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); |
| 280 | QSignalSpy binaryMessageReceivedSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); |
| 281 | QSignalSpy textFrameReceivedSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); |
| 282 | QSignalSpy textMessageReceivedSpy(&dataProcessor, SIGNAL(textMessageReceived(QString))); |
| 283 | dataProcessor.process(pIoDevice: &buffer); |
| 284 | QCOMPARE(errorReceivedSpy.count(), 0); |
| 285 | QCOMPARE(pingReceivedSpy.count(), 0); |
| 286 | QCOMPARE(pongReceivedSpy.count(), 0); |
| 287 | QCOMPARE(closeReceivedSpy.count(), 0); |
| 288 | QCOMPARE(binaryFrameReceivedSpy.count(), 1); |
| 289 | QCOMPARE(binaryMessageReceivedSpy.count(), 1); |
| 290 | QCOMPARE(textFrameReceivedSpy.count(), 0); |
| 291 | QCOMPARE(textMessageReceivedSpy.count(), 0); |
| 292 | QList<QVariant> arguments = binaryFrameReceivedSpy.takeFirst(); |
| 293 | QCOMPARE(arguments.at(0).toByteArray().length(), payload.length()); |
| 294 | arguments = binaryMessageReceivedSpy.takeFirst(); |
| 295 | QCOMPARE(arguments.at(0).toByteArray().length(), payload.length()); |
| 296 | buffer.close(); |
| 297 | } |
| 298 | |
| 299 | void tst_DataProcessor::goodTextFrame_data() |
| 300 | { |
| 301 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 302 | QTest::addColumn<int>(name: "size" ); |
| 303 | |
| 304 | //test frames with small (< 126), large ( < 65536) and big ( > 65535) payloads |
| 305 | for (int i = 0; i < (65536 + 256); i += 128) |
| 306 | { |
| 307 | QTest::newRow(QStringLiteral("Text frame with %1 ASCII characters" ) |
| 308 | .arg(a: i).toLatin1().constData()) << QByteArray(i, 'a') << i; |
| 309 | } |
| 310 | //test all valid ASCII characters |
| 311 | for (int i = 0; i < 128; ++i) |
| 312 | { |
| 313 | QTest::newRow(QStringLiteral("Text frame with containing ASCII character '0x%1'" ) |
| 314 | .arg(a: QByteArray(1, char(i)).toHex().constData()).toLatin1().constData()) |
| 315 | << QByteArray(1, char(i)) << 1; |
| 316 | } |
| 317 | |
| 318 | //the text string reads: Text frame containing Hello-µ@ßöäüà á-UTF-8!! |
| 319 | //Visual Studio doesn't like UTF-8 in source code, so we use escape codes for the string |
| 320 | //The number 22 refers to the length of the string; |
| 321 | //the length was incorrectly calculated on Visual Studio |
| 322 | |
| 323 | //doing extensive QStringLiteral concatenations here, because |
| 324 | //MSVC 2010 complains when using concatenation literal strings about |
| 325 | //concatenation of wide and narrow strings: |
| 326 | //error C2308: concatenating mismatched strings |
| 327 | QTest::newRow(dataTag: (QStringLiteral("Text frame containing Hello-" ) + |
| 328 | QStringLiteral("\xC2\xB5\x40\xC3\x9F\xC3\xB6\xC3\xA4\xC3\xBC\xC3\xA0" ) + |
| 329 | QStringLiteral("\xC3\xA1-UTF-8!!" )).toUtf8().constData()) |
| 330 | << QByteArray::fromHex(hexEncoded: "48656c6c6f2dc2b540c39fc3b6c3a4c3bcc3a0c3a12d5554462d382121" ) |
| 331 | << 22; |
| 332 | } |
| 333 | |
| 334 | void tst_DataProcessor::goodTextFrame() |
| 335 | { |
| 336 | QByteArray data; |
| 337 | QBuffer buffer; |
| 338 | QWebSocketDataProcessor dataProcessor; |
| 339 | QFETCH(QByteArray, payload); |
| 340 | QFETCH(int, size); |
| 341 | |
| 342 | data.append(c: char(FIN | QWebSocketProtocol::OpCodeText)); |
| 343 | |
| 344 | if (payload.length() < 126) |
| 345 | { |
| 346 | data.append(c: char(payload.length())); |
| 347 | } |
| 348 | else if (payload.length() < 65536) |
| 349 | { |
| 350 | quint16 swapped = qToBigEndian<quint16>(source: payload.length()); |
| 351 | const char *wireRepresentation |
| 352 | = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 353 | data.append(c: char(126)).append(s: wireRepresentation, len: 2); |
| 354 | } |
| 355 | else |
| 356 | { |
| 357 | quint64 swapped = qToBigEndian<quint64>(source: payload.length()); |
| 358 | const char *wireRepresentation |
| 359 | = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 360 | data.append(c: char(127)).append(s: wireRepresentation, len: 8); |
| 361 | } |
| 362 | |
| 363 | data.append(a: payload); |
| 364 | buffer.setData(data); |
| 365 | buffer.open(openMode: QIODevice::ReadOnly); |
| 366 | |
| 367 | QSignalSpy errorReceivedSpy(&dataProcessor, |
| 368 | SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); |
| 369 | QSignalSpy closeReceivedSpy(&dataProcessor, |
| 370 | SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); |
| 371 | QSignalSpy pingReceivedSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray))); |
| 372 | QSignalSpy pongReceivedSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray))); |
| 373 | QSignalSpy textFrameReceivedSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); |
| 374 | QSignalSpy textMessageReceivedSpy(&dataProcessor, SIGNAL(textMessageReceived(QString))); |
| 375 | QSignalSpy binaryFrameReceivedSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); |
| 376 | QSignalSpy binaryMessageReceivedSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); |
| 377 | |
| 378 | dataProcessor.process(pIoDevice: &buffer); |
| 379 | |
| 380 | QCOMPARE(errorReceivedSpy.count(), 0); |
| 381 | QCOMPARE(pingReceivedSpy.count(), 0); |
| 382 | QCOMPARE(pongReceivedSpy.count(), 0); |
| 383 | QCOMPARE(closeReceivedSpy.count(), 0); |
| 384 | QCOMPARE(textFrameReceivedSpy.count(), 1); |
| 385 | QCOMPARE(textMessageReceivedSpy.count(), 1); |
| 386 | QCOMPARE(binaryFrameReceivedSpy.count(), 0); |
| 387 | QCOMPARE(binaryMessageReceivedSpy.count(), 0); |
| 388 | QList<QVariant> arguments = textFrameReceivedSpy.takeFirst(); |
| 389 | QCOMPARE(arguments.at(0).toString().length(), size); |
| 390 | arguments = textMessageReceivedSpy.takeFirst(); |
| 391 | QCOMPARE(arguments.at(0).toString().length(), size); |
| 392 | buffer.close(); |
| 393 | } |
| 394 | |
| 395 | void tst_DataProcessor::goodControlFrame() |
| 396 | { |
| 397 | QByteArray data; |
| 398 | QBuffer buffer; |
| 399 | QWebSocketDataProcessor dataProcessor; |
| 400 | |
| 401 | QSignalSpy closeFrameReceivedSpy(&dataProcessor, |
| 402 | SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); |
| 403 | QSignalSpy errorReceivedSpy(&dataProcessor, |
| 404 | SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); |
| 405 | QSignalSpy textFrameReceivedSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); |
| 406 | QSignalSpy textMessageReceivedSpy(&dataProcessor, SIGNAL(textMessageReceived(QString))); |
| 407 | QSignalSpy binaryFrameReceivedSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); |
| 408 | QSignalSpy binaryMessageReceivedSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); |
| 409 | QSignalSpy pingReceivedSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray))); |
| 410 | QSignalSpy pongReceivedSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray))); |
| 411 | |
| 412 | data.append(c: char(FIN | QWebSocketProtocol::OpCodePing)); |
| 413 | data.append(s: QChar::fromLatin1(c: 0)); |
| 414 | buffer.setData(data); |
| 415 | buffer.open(openMode: QIODevice::ReadOnly); |
| 416 | dataProcessor.process(pIoDevice: &buffer); |
| 417 | QCOMPARE(errorReceivedSpy.count(), 0); |
| 418 | QCOMPARE(textFrameReceivedSpy.count(), 0); |
| 419 | QCOMPARE(textMessageReceivedSpy.count(), 0); |
| 420 | QCOMPARE(binaryFrameReceivedSpy.count(), 0); |
| 421 | QCOMPARE(binaryMessageReceivedSpy.count(), 0); |
| 422 | QCOMPARE(closeFrameReceivedSpy.count(), 0); |
| 423 | QCOMPARE(pongReceivedSpy.count(), 0); |
| 424 | QCOMPARE(pingReceivedSpy.count(), 1); |
| 425 | buffer.close(); |
| 426 | |
| 427 | data.clear(); |
| 428 | pingReceivedSpy.clear(); |
| 429 | pongReceivedSpy.clear(); |
| 430 | data.append(c: char(FIN | QWebSocketProtocol::OpCodePong)); |
| 431 | data.append(s: QChar::fromLatin1(c: 0)); |
| 432 | buffer.setData(data); |
| 433 | buffer.open(openMode: QIODevice::ReadOnly); |
| 434 | dataProcessor.process(pIoDevice: &buffer); |
| 435 | QCOMPARE(errorReceivedSpy.count(), 0); |
| 436 | QCOMPARE(textFrameReceivedSpy.count(), 0); |
| 437 | QCOMPARE(textMessageReceivedSpy.count(), 0); |
| 438 | QCOMPARE(binaryFrameReceivedSpy.count(), 0); |
| 439 | QCOMPARE(binaryMessageReceivedSpy.count(), 0); |
| 440 | QCOMPARE(closeFrameReceivedSpy.count(), 0); |
| 441 | QCOMPARE(pingReceivedSpy.count(), 0); |
| 442 | QCOMPARE(pongReceivedSpy.count(), 1); |
| 443 | buffer.close(); |
| 444 | } |
| 445 | |
| 446 | void tst_DataProcessor::goodCloseFrame_data() |
| 447 | { |
| 448 | QTest::addColumn<QString>(name: "payload" ); |
| 449 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "closeCode" ); |
| 450 | //control frame data cannot exceed 125 bytes; smaller than 124, |
| 451 | //because we also need a 2 byte close code |
| 452 | for (int i = 0; i < 124; ++i) |
| 453 | { |
| 454 | QTest::newRow(QStringLiteral("Close frame with %1 ASCII characters" ) |
| 455 | .arg(a: i).toLatin1().constData()) |
| 456 | << QString(i, 'a') |
| 457 | << QWebSocketProtocol::CloseCodeNormal; |
| 458 | } |
| 459 | for (int i = 0; i < 126; ++i) |
| 460 | { |
| 461 | QTest::newRow(QStringLiteral("Text frame with containing ASCII character '0x%1'" ) |
| 462 | .arg(a: QByteArray(1, char(i)).toHex().constData()).toLatin1().constData()) |
| 463 | << QString(1, char(i)) << QWebSocketProtocol::CloseCodeNormal; |
| 464 | } |
| 465 | QTest::newRow(dataTag: "Close frame with close code NORMAL" ) |
| 466 | << QString(1, 'a') << QWebSocketProtocol::CloseCodeNormal; |
| 467 | QTest::newRow(dataTag: "Close frame with close code BAD OPERATION" ) |
| 468 | << QString(1, 'a') << QWebSocketProtocol::CloseCodeBadOperation; |
| 469 | QTest::newRow(dataTag: "Close frame with close code DATATYPE NOT SUPPORTED" ) |
| 470 | << QString(1, 'a') << QWebSocketProtocol::CloseCodeDatatypeNotSupported; |
| 471 | QTest::newRow(dataTag: "Close frame with close code GOING AWAY" ) |
| 472 | << QString(1, 'a') << QWebSocketProtocol::CloseCodeGoingAway; |
| 473 | QTest::newRow(dataTag: "Close frame with close code MISSING EXTENSION" ) |
| 474 | << QString(1, 'a') << QWebSocketProtocol::CloseCodeMissingExtension; |
| 475 | QTest::newRow(dataTag: "Close frame with close code POLICY VIOLATED" ) |
| 476 | << QString(1, 'a') << QWebSocketProtocol::CloseCodePolicyViolated; |
| 477 | QTest::newRow(dataTag: "Close frame with close code PROTOCOL ERROR" ) |
| 478 | << QString(1, 'a') << QWebSocketProtocol::CloseCodeProtocolError; |
| 479 | QTest::newRow(dataTag: "Close frame with close code TOO MUCH DATA" ) |
| 480 | << QString(1, 'a') << QWebSocketProtocol::CloseCodeTooMuchData; |
| 481 | QTest::newRow(dataTag: "Close frame with close code WRONG DATATYPE" ) |
| 482 | << QString(1, 'a') << QWebSocketProtocol::CloseCodeWrongDatatype; |
| 483 | QTest::newRow(dataTag: "Close frame with close code 3000" ) |
| 484 | << QString(1, 'a') << QWebSocketProtocol::CloseCode(3000); |
| 485 | QTest::newRow(dataTag: "Close frame with close code 3999" ) |
| 486 | << QString(1, 'a') << QWebSocketProtocol::CloseCode(3999); |
| 487 | QTest::newRow(dataTag: "Close frame with close code 4000" ) |
| 488 | << QString(1, 'a') << QWebSocketProtocol::CloseCode(4000); |
| 489 | QTest::newRow(dataTag: "Close frame with close code 4999" ) |
| 490 | << QString(1, 'a') << QWebSocketProtocol::CloseCode(4999); |
| 491 | |
| 492 | //close frames with no close reason |
| 493 | QTest::newRow(dataTag: "Close frame with close code NORMAL and no reason" ) |
| 494 | << QString() << QWebSocketProtocol::CloseCodeNormal; |
| 495 | QTest::newRow(dataTag: "Close frame with close code BAD OPERATION and no reason" ) |
| 496 | << QString() << QWebSocketProtocol::CloseCodeBadOperation; |
| 497 | QTest::newRow(dataTag: "Close frame with close code DATATYPE NOT SUPPORTED and no reason" ) |
| 498 | << QString() << QWebSocketProtocol::CloseCodeDatatypeNotSupported; |
| 499 | QTest::newRow(dataTag: "Close frame with close code GOING AWAY and no reason" ) |
| 500 | << QString() << QWebSocketProtocol::CloseCodeGoingAway; |
| 501 | QTest::newRow(dataTag: "Close frame with close code MISSING EXTENSION and no reason" ) |
| 502 | << QString() << QWebSocketProtocol::CloseCodeMissingExtension; |
| 503 | QTest::newRow(dataTag: "Close frame with close code POLICY VIOLATED and no reason" ) |
| 504 | << QString() << QWebSocketProtocol::CloseCodePolicyViolated; |
| 505 | QTest::newRow(dataTag: "Close frame with close code PROTOCOL ERROR and no reason" ) |
| 506 | << QString() << QWebSocketProtocol::CloseCodeProtocolError; |
| 507 | QTest::newRow(dataTag: "Close frame with close code TOO MUCH DATA and no reason" ) |
| 508 | << QString() << QWebSocketProtocol::CloseCodeTooMuchData; |
| 509 | QTest::newRow(dataTag: "Close frame with close code WRONG DATATYPE and no reason" ) |
| 510 | << QString() << QWebSocketProtocol::CloseCodeWrongDatatype; |
| 511 | QTest::newRow(dataTag: "Close frame with close code 3000 and no reason" ) |
| 512 | << QString() << QWebSocketProtocol::CloseCode(3000); |
| 513 | QTest::newRow(dataTag: "Close frame with close code 3999 and no reason" ) |
| 514 | << QString() << QWebSocketProtocol::CloseCode(3999); |
| 515 | QTest::newRow(dataTag: "Close frame with close code 4000 and no reason" ) |
| 516 | << QString() << QWebSocketProtocol::CloseCode(4000); |
| 517 | QTest::newRow(dataTag: "Close frame with close code 4999 and no reason" ) |
| 518 | << QString() << QWebSocketProtocol::CloseCode(4999); |
| 519 | |
| 520 | QTest::newRow(dataTag: "Close frame with no close code and no reason" ) |
| 521 | << QString() << QWebSocketProtocol::CloseCode(0); |
| 522 | } |
| 523 | |
| 524 | void tst_DataProcessor::goodOpcodes_data() |
| 525 | { |
| 526 | QTest::addColumn<QWebSocketProtocol::OpCode>(name: "opCode" ); |
| 527 | |
| 528 | QTest::newRow(dataTag: "Frame with PING opcode" ) << QWebSocketProtocol::OpCodePing; |
| 529 | QTest::newRow(dataTag: "Frame with PONG opcode" ) << QWebSocketProtocol::OpCodePong; |
| 530 | QTest::newRow(dataTag: "Frame with TEXT opcode" ) << QWebSocketProtocol::OpCodeText; |
| 531 | QTest::newRow(dataTag: "Frame with BINARY opcode" ) << QWebSocketProtocol::OpCodeBinary; |
| 532 | QTest::newRow(dataTag: "Frame with CLOSE opcode" ) << QWebSocketProtocol::OpCodeClose; |
| 533 | } |
| 534 | |
| 535 | void tst_DataProcessor::goodOpcodes() |
| 536 | { |
| 537 | QByteArray data; |
| 538 | QBuffer buffer; |
| 539 | QWebSocketDataProcessor dataProcessor; |
| 540 | QFETCH(QWebSocketProtocol::OpCode, opCode); |
| 541 | |
| 542 | data.append(c: char(FIN | opCode)); |
| 543 | data.append(c: char(0)); //zero length |
| 544 | |
| 545 | buffer.setData(data); |
| 546 | buffer.open(openMode: QIODevice::ReadOnly); |
| 547 | |
| 548 | QSignalSpy errorReceivedSpy(&dataProcessor, |
| 549 | SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); |
| 550 | QSignalSpy closeReceivedSpy(&dataProcessor, |
| 551 | SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); |
| 552 | QSignalSpy pingReceivedSpy(&dataProcessor, |
| 553 | SIGNAL(pingReceived(QByteArray))); |
| 554 | QSignalSpy pongReceivedSpy(&dataProcessor, |
| 555 | SIGNAL(pongReceived(QByteArray))); |
| 556 | QSignalSpy textFrameReceivedSpy(&dataProcessor, |
| 557 | SIGNAL(textFrameReceived(QString,bool))); |
| 558 | QSignalSpy textMessageReceivedSpy(&dataProcessor, |
| 559 | SIGNAL(textMessageReceived(QString))); |
| 560 | QSignalSpy binaryFrameReceivedSpy(&dataProcessor, |
| 561 | SIGNAL(binaryFrameReceived(QByteArray,bool))); |
| 562 | QSignalSpy binaryMessageReceivedSpy(&dataProcessor, |
| 563 | SIGNAL(binaryMessageReceived(QByteArray))); |
| 564 | |
| 565 | dataProcessor.process(pIoDevice: &buffer); |
| 566 | |
| 567 | QCOMPARE(errorReceivedSpy.count(), 0); |
| 568 | QCOMPARE(pingReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodePing ? 1 : 0); |
| 569 | QCOMPARE(pongReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodePong ? 1 : 0); |
| 570 | QCOMPARE(closeReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodeClose ? 1 : 0); |
| 571 | QCOMPARE(textFrameReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodeText ? 1 : 0); |
| 572 | QCOMPARE(textMessageReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodeText ? 1 : 0); |
| 573 | QCOMPARE(binaryFrameReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodeBinary ? 1 : 0); |
| 574 | QCOMPARE(binaryMessageReceivedSpy.count(), opCode == QWebSocketProtocol::OpCodeBinary ? 1 : 0); |
| 575 | |
| 576 | buffer.close(); |
| 577 | } |
| 578 | |
| 579 | void tst_DataProcessor::goodCloseFrame() |
| 580 | { |
| 581 | QByteArray data; |
| 582 | QBuffer buffer; |
| 583 | QWebSocketDataProcessor dataProcessor; |
| 584 | QFETCH(QString, payload); |
| 585 | QFETCH(QWebSocketProtocol::CloseCode, closeCode); |
| 586 | quint16 swapped = qToBigEndian<quint16>(source: closeCode); |
| 587 | const char *wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 588 | |
| 589 | data.append(c: char(FIN | QWebSocketProtocol::OpCodeClose)); |
| 590 | if (swapped != 0) |
| 591 | { |
| 592 | data.append(c: char(payload.length() + 2)).append(s: wireRepresentation, len: 2).append(s: payload); |
| 593 | } |
| 594 | else |
| 595 | { |
| 596 | data.append(s: QChar::fromLatin1(c: 0)); //payload length 0; |
| 597 | //dataprocessor emits a CloseCodeNormal close code when none is present |
| 598 | closeCode = QWebSocketProtocol::CloseCodeNormal; |
| 599 | } |
| 600 | buffer.setData(data); |
| 601 | buffer.open(openMode: QIODevice::ReadOnly); |
| 602 | |
| 603 | QSignalSpy errorReceivedSpy(&dataProcessor, |
| 604 | SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); |
| 605 | QSignalSpy pingReceivedSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray))); |
| 606 | QSignalSpy pongReceivedSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray))); |
| 607 | QSignalSpy closeFrameReceivedSpy(&dataProcessor, |
| 608 | SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); |
| 609 | QSignalSpy textFrameReceivedSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); |
| 610 | QSignalSpy textMessageReceivedSpy(&dataProcessor, SIGNAL(textMessageReceived(QString))); |
| 611 | QSignalSpy binaryFrameReceivedSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); |
| 612 | QSignalSpy binaryMessageReceivedSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); |
| 613 | |
| 614 | dataProcessor.process(pIoDevice: &buffer); |
| 615 | |
| 616 | QCOMPARE(errorReceivedSpy.count(), 0); |
| 617 | QCOMPARE(pingReceivedSpy.count(), 0); |
| 618 | QCOMPARE(pongReceivedSpy.count(), 0); |
| 619 | QCOMPARE(closeFrameReceivedSpy.count(), 1); |
| 620 | QCOMPARE(textFrameReceivedSpy.count(), 0); |
| 621 | QCOMPARE(textMessageReceivedSpy.count(), 0); |
| 622 | QCOMPARE(binaryFrameReceivedSpy.count(), 0); |
| 623 | QCOMPARE(binaryMessageReceivedSpy.count(), 0); |
| 624 | QList<QVariant> arguments = closeFrameReceivedSpy.takeFirst(); |
| 625 | QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), closeCode); |
| 626 | QCOMPARE(arguments.at(1).toString().length(), payload.length()); |
| 627 | buffer.close(); |
| 628 | } |
| 629 | |
| 630 | void tst_DataProcessor::nonCharacterCodes_data() |
| 631 | { |
| 632 | QTest::addColumn<quint8>(name: "firstByte" ); |
| 633 | QTest::addColumn<quint8>(name: "secondByte" ); |
| 634 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 635 | QTest::addColumn<bool>(name: "isContinuationFrame" ); |
| 636 | |
| 637 | nonCharacterSequence(sequence: "efbfbe" ); |
| 638 | nonCharacterSequence(sequence: "efbfbf" ); |
| 639 | nonCharacterSequence(sequence: "f09fbfbe" ); |
| 640 | nonCharacterSequence(sequence: "f09fbfbf" ); |
| 641 | nonCharacterSequence(sequence: "f0afbfbe" ); |
| 642 | nonCharacterSequence(sequence: "f0afbfbf" ); |
| 643 | nonCharacterSequence(sequence: "f0bfbfbe" ); |
| 644 | nonCharacterSequence(sequence: "f0bfbfbf" ); |
| 645 | nonCharacterSequence(sequence: "f18fbfbe" ); |
| 646 | nonCharacterSequence(sequence: "f18fbfbf" ); |
| 647 | nonCharacterSequence(sequence: "f19fbfbe" ); |
| 648 | nonCharacterSequence(sequence: "f19fbfbf" ); |
| 649 | nonCharacterSequence(sequence: "f1afbfbe" ); |
| 650 | nonCharacterSequence(sequence: "f1afbfbf" ); |
| 651 | nonCharacterSequence(sequence: "f1bfbfbe" ); |
| 652 | nonCharacterSequence(sequence: "f1bfbfbf" ); |
| 653 | nonCharacterSequence(sequence: "f28fbfbe" ); |
| 654 | nonCharacterSequence(sequence: "f28fbfbf" ); |
| 655 | nonCharacterSequence(sequence: "f29fbfbe" ); |
| 656 | nonCharacterSequence(sequence: "f29fbfbf" ); |
| 657 | nonCharacterSequence(sequence: "f2afbfbe" ); |
| 658 | nonCharacterSequence(sequence: "f2afbfbf" ); |
| 659 | nonCharacterSequence(sequence: "f2bfbfbe" ); |
| 660 | nonCharacterSequence(sequence: "f2bfbfbf" ); |
| 661 | nonCharacterSequence(sequence: "f38fbfbe" ); |
| 662 | nonCharacterSequence(sequence: "f38fbfbf" ); |
| 663 | nonCharacterSequence(sequence: "f39fbfbe" ); |
| 664 | nonCharacterSequence(sequence: "f39fbfbf" ); |
| 665 | nonCharacterSequence(sequence: "f3afbfbe" ); |
| 666 | nonCharacterSequence(sequence: "f3afbfbf" ); |
| 667 | nonCharacterSequence(sequence: "f3bfbfbe" ); |
| 668 | nonCharacterSequence(sequence: "f3bfbfbf" ); |
| 669 | nonCharacterSequence(sequence: "f48fbfbe" ); |
| 670 | nonCharacterSequence(sequence: "f48fbfbf" ); |
| 671 | } |
| 672 | |
| 673 | void tst_DataProcessor::nonCharacterCodes() |
| 674 | { |
| 675 | QFETCH(quint8, firstByte); |
| 676 | QFETCH(quint8, secondByte); |
| 677 | QFETCH(QByteArray, payload); |
| 678 | QFETCH(bool, isContinuationFrame); |
| 679 | |
| 680 | if (!isContinuationFrame) |
| 681 | { |
| 682 | QByteArray data; |
| 683 | QBuffer buffer; |
| 684 | QWebSocketDataProcessor dataProcessor; |
| 685 | QSignalSpy errorSpy(&dataProcessor, |
| 686 | SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); |
| 687 | QSignalSpy closeSpy(&dataProcessor, |
| 688 | SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); |
| 689 | QSignalSpy pingFrameSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray))); |
| 690 | QSignalSpy pongFrameSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray))); |
| 691 | QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); |
| 692 | QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString))); |
| 693 | QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); |
| 694 | QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); |
| 695 | |
| 696 | data.append(c: firstByte).append(c: secondByte); |
| 697 | data.append(a: payload); |
| 698 | buffer.setData(data); |
| 699 | buffer.open(openMode: QIODevice::ReadOnly); |
| 700 | dataProcessor.process(pIoDevice: &buffer); |
| 701 | |
| 702 | QCOMPARE(errorSpy.count(), 0); |
| 703 | QCOMPARE(closeSpy.count(), 0); |
| 704 | QCOMPARE(pingFrameSpy.count(), 0); |
| 705 | QCOMPARE(pongFrameSpy.count(), 0); |
| 706 | QCOMPARE(textFrameSpy.count(), 1); |
| 707 | QCOMPARE(textMessageSpy.count(), 1); |
| 708 | QCOMPARE(binaryFrameSpy.count(), 0); |
| 709 | QCOMPARE(binaryMessageSpy.count(), 0); |
| 710 | |
| 711 | QVariantList arguments = textFrameSpy.takeFirst(); |
| 712 | QCOMPARE(arguments.at(0).value<QString>().toUtf8(), payload); |
| 713 | QCOMPARE(arguments.at(1).value<bool>(), !isContinuationFrame); |
| 714 | arguments = textMessageSpy.takeFirst(); |
| 715 | QCOMPARE(arguments.at(0).value<QString>().toUtf8(), payload); |
| 716 | buffer.close(); |
| 717 | } |
| 718 | } |
| 719 | |
| 720 | void tst_DataProcessor::frameTooSmall() |
| 721 | { |
| 722 | QByteArray data; |
| 723 | QBuffer buffer; |
| 724 | QWebSocketDataProcessor dataProcessor; |
| 725 | QByteArray firstFrame; |
| 726 | |
| 727 | firstFrame.append(c: quint8(QWebSocketProtocol::OpCodeText)).append(c: char(1)) |
| 728 | .append(a: QByteArray(1, 'a')); |
| 729 | |
| 730 | //with nothing in the buffer, the dataProcessor should time out |
| 731 | //and the error should be CloseCodeGoingAway meaning the socket will be closed |
| 732 | buffer.setData(data); |
| 733 | buffer.open(openMode: QIODevice::ReadOnly); |
| 734 | QSignalSpy errorSpy(&dataProcessor, |
| 735 | SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); |
| 736 | QSignalSpy closeSpy(&dataProcessor, |
| 737 | SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); |
| 738 | QSignalSpy pingMessageSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray))); |
| 739 | QSignalSpy pongMessageSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray))); |
| 740 | QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString))); |
| 741 | QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); |
| 742 | QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); |
| 743 | QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); |
| 744 | |
| 745 | dataProcessor.process(pIoDevice: &buffer); |
| 746 | |
| 747 | QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000); |
| 748 | QCOMPARE(errorSpy.count(), 1); |
| 749 | QCOMPARE(closeSpy.count(), 0); |
| 750 | QCOMPARE(pingMessageSpy.count(), 0); |
| 751 | QCOMPARE(pongMessageSpy.count(), 0); |
| 752 | QCOMPARE(textMessageSpy.count(), 0); |
| 753 | QCOMPARE(binaryMessageSpy.count(), 0); |
| 754 | QCOMPARE(textFrameSpy.count(), 0); |
| 755 | QCOMPARE(binaryFrameSpy.count(), 0); |
| 756 | |
| 757 | QList<QVariant> arguments = errorSpy.takeFirst(); |
| 758 | QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), |
| 759 | QWebSocketProtocol::CloseCodeGoingAway); |
| 760 | errorSpy.clear(); |
| 761 | closeSpy.clear(); |
| 762 | pingMessageSpy.clear(); |
| 763 | pongMessageSpy.clear(); |
| 764 | textMessageSpy.clear(); |
| 765 | binaryMessageSpy.clear(); |
| 766 | textFrameSpy.clear(); |
| 767 | binaryFrameSpy.clear(); |
| 768 | buffer.close(); |
| 769 | data.clear(); |
| 770 | |
| 771 | //only one byte; this is far too little; |
| 772 | //should get a time out as well and the error should be CloseCodeGoingAway |
| 773 | //meaning the socket will be closed |
| 774 | data.append(c: quint8('1')); //put 1 byte in the buffer; this is too little |
| 775 | buffer.setData(data); |
| 776 | buffer.open(openMode: QIODevice::ReadOnly); |
| 777 | |
| 778 | dataProcessor.process(pIoDevice: &buffer); |
| 779 | |
| 780 | QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000); |
| 781 | QCOMPARE(errorSpy.count(), 1); |
| 782 | QCOMPARE(closeSpy.count(), 0); |
| 783 | QCOMPARE(pingMessageSpy.count(), 0); |
| 784 | QCOMPARE(pongMessageSpy.count(), 0); |
| 785 | QCOMPARE(textMessageSpy.count(), 0); |
| 786 | QCOMPARE(binaryMessageSpy.count(), 0); |
| 787 | QCOMPARE(textFrameSpy.count(), 0); |
| 788 | QCOMPARE(binaryFrameSpy.count(), 0); |
| 789 | |
| 790 | arguments = errorSpy.takeFirst(); |
| 791 | QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), |
| 792 | QWebSocketProtocol::CloseCodeGoingAway); |
| 793 | buffer.close(); |
| 794 | errorSpy.clear(); |
| 795 | closeSpy.clear(); |
| 796 | pingMessageSpy.clear(); |
| 797 | pongMessageSpy.clear(); |
| 798 | textMessageSpy.clear(); |
| 799 | binaryMessageSpy.clear(); |
| 800 | textFrameSpy.clear(); |
| 801 | binaryFrameSpy.clear(); |
| 802 | data.clear(); |
| 803 | |
| 804 | |
| 805 | { |
| 806 | //text frame with final bit not set |
| 807 | data.append(c: char(QWebSocketProtocol::OpCodeText)).append(c: char(0x0)); |
| 808 | buffer.setData(data); |
| 809 | buffer.open(openMode: QIODevice::ReadOnly); |
| 810 | |
| 811 | dataProcessor.process(pIoDevice: &buffer); |
| 812 | |
| 813 | QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000); |
| 814 | QCOMPARE(errorSpy.count(), 1); |
| 815 | QCOMPARE(closeSpy.count(), 0); |
| 816 | QCOMPARE(pingMessageSpy.count(), 0); |
| 817 | QCOMPARE(pongMessageSpy.count(), 0); |
| 818 | QCOMPARE(textMessageSpy.count(), 0); |
| 819 | QCOMPARE(binaryMessageSpy.count(), 0); |
| 820 | QCOMPARE(textFrameSpy.count(), 1); |
| 821 | QCOMPARE(binaryFrameSpy.count(), 0); |
| 822 | |
| 823 | errorSpy.clear(); |
| 824 | closeSpy.clear(); |
| 825 | pingMessageSpy.clear(); |
| 826 | pongMessageSpy.clear(); |
| 827 | textMessageSpy.clear(); |
| 828 | binaryMessageSpy.clear(); |
| 829 | textFrameSpy.clear(); |
| 830 | binaryFrameSpy.clear(); |
| 831 | buffer.close(); |
| 832 | data.clear(); |
| 833 | |
| 834 | //with nothing in the buffer, |
| 835 | //the dataProcessor should time out and the error should be CloseCodeGoingAway |
| 836 | //meaning the socket will be closed |
| 837 | buffer.setData(data); |
| 838 | buffer.open(openMode: QIODevice::ReadOnly); |
| 839 | dataProcessor.process(pIoDevice: &buffer); |
| 840 | |
| 841 | QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000); |
| 842 | QCOMPARE(errorSpy.count(), 1); |
| 843 | QCOMPARE(closeSpy.count(), 0); |
| 844 | QCOMPARE(pingMessageSpy.count(), 0); |
| 845 | QCOMPARE(pongMessageSpy.count(), 0); |
| 846 | QCOMPARE(textMessageSpy.count(), 0); |
| 847 | QCOMPARE(binaryMessageSpy.count(), 0); |
| 848 | QCOMPARE(textFrameSpy.count(), 0); |
| 849 | QCOMPARE(binaryFrameSpy.count(), 0); |
| 850 | |
| 851 | QList<QVariant> arguments = errorSpy.takeFirst(); |
| 852 | QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), |
| 853 | QWebSocketProtocol::CloseCodeGoingAway); |
| 854 | errorSpy.clear(); |
| 855 | closeSpy.clear(); |
| 856 | pingMessageSpy.clear(); |
| 857 | pongMessageSpy.clear(); |
| 858 | textMessageSpy.clear(); |
| 859 | binaryMessageSpy.clear(); |
| 860 | textFrameSpy.clear(); |
| 861 | binaryFrameSpy.clear(); |
| 862 | buffer.close(); |
| 863 | data.clear(); |
| 864 | |
| 865 | //text frame with final bit not set |
| 866 | data.append(c: char(QWebSocketProtocol::OpCodeText)).append(c: char(0x0)); |
| 867 | buffer.setData(data); |
| 868 | buffer.open(openMode: QIODevice::ReadOnly); |
| 869 | dataProcessor.process(pIoDevice: &buffer); |
| 870 | |
| 871 | QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000); |
| 872 | QCOMPARE(errorSpy.count(), 1); |
| 873 | QCOMPARE(closeSpy.count(), 0); |
| 874 | QCOMPARE(pingMessageSpy.count(), 0); |
| 875 | QCOMPARE(pongMessageSpy.count(), 0); |
| 876 | QCOMPARE(textMessageSpy.count(), 0); |
| 877 | QCOMPARE(binaryMessageSpy.count(), 0); |
| 878 | QCOMPARE(textFrameSpy.count(), 1); |
| 879 | QCOMPARE(binaryFrameSpy.count(), 0); |
| 880 | |
| 881 | buffer.close(); |
| 882 | data.clear(); |
| 883 | |
| 884 | errorSpy.clear(); |
| 885 | closeSpy.clear(); |
| 886 | pingMessageSpy.clear(); |
| 887 | pongMessageSpy.clear(); |
| 888 | textMessageSpy.clear(); |
| 889 | binaryMessageSpy.clear(); |
| 890 | textFrameSpy.clear(); |
| 891 | binaryFrameSpy.clear(); |
| 892 | |
| 893 | //only 1 byte follows in continuation frame; |
| 894 | //should time out with close code CloseCodeGoingAway |
| 895 | data.append(c: 'a'); |
| 896 | buffer.setData(data); |
| 897 | buffer.open(openMode: QIODevice::ReadOnly); |
| 898 | |
| 899 | dataProcessor.process(pIoDevice: &buffer); |
| 900 | QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), 7000); |
| 901 | QCOMPARE(errorSpy.count(), 1); |
| 902 | QCOMPARE(closeSpy.count(), 0); |
| 903 | QCOMPARE(pingMessageSpy.count(), 0); |
| 904 | QCOMPARE(pongMessageSpy.count(), 0); |
| 905 | QCOMPARE(textMessageSpy.count(), 0); |
| 906 | QCOMPARE(binaryMessageSpy.count(), 0); |
| 907 | QCOMPARE(textFrameSpy.count(), 0); |
| 908 | QCOMPARE(binaryFrameSpy.count(), 0); |
| 909 | arguments = errorSpy.takeFirst(); |
| 910 | QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), |
| 911 | QWebSocketProtocol::CloseCodeGoingAway); |
| 912 | buffer.close(); |
| 913 | } |
| 914 | } |
| 915 | |
| 916 | void tst_DataProcessor::frameTooBig_data() |
| 917 | { |
| 918 | QTest::addColumn<quint8>(name: "firstByte" ); |
| 919 | QTest::addColumn<quint8>(name: "secondByte" ); |
| 920 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 921 | QTest::addColumn<bool>(name: "isContinuationFrame" ); |
| 922 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "expectedCloseCode" ); |
| 923 | |
| 924 | quint64 swapped64 = 0; |
| 925 | const char *wireRepresentation = nullptr; |
| 926 | |
| 927 | //only data frames are checked for being too big |
| 928 | //control frames have explicit checking on a maximum payload size of 125, |
| 929 | //which is tested elsewhere |
| 930 | |
| 931 | swapped64 = qToBigEndian<quint64>(source: QWebSocketDataProcessor::maxFrameSize() + 1); |
| 932 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped64)); |
| 933 | QTest::newRow(dataTag: "Text frame with payload size > INT_MAX" ) |
| 934 | << quint8(FIN | QWebSocketProtocol::OpCodeText) |
| 935 | << quint8(127) |
| 936 | << QByteArray(wireRepresentation, 8).append(a: QByteArray(32, 'a')) |
| 937 | << false |
| 938 | << QWebSocketProtocol::CloseCodeTooMuchData; |
| 939 | |
| 940 | swapped64 = qToBigEndian<quint64>(source: QWebSocketDataProcessor::maxFrameSize() + 1); |
| 941 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped64)); |
| 942 | QTest::newRow(dataTag: "Binary frame with payload size > INT_MAX" ) |
| 943 | << quint8(FIN | QWebSocketProtocol::OpCodeBinary) |
| 944 | << quint8(127) |
| 945 | << QByteArray(wireRepresentation, 8).append(a: QByteArray(32, 'a')) |
| 946 | << false |
| 947 | << QWebSocketProtocol::CloseCodeTooMuchData; |
| 948 | |
| 949 | swapped64 = qToBigEndian<quint64>(source: QWebSocketDataProcessor::maxFrameSize() + 1); |
| 950 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped64)); |
| 951 | QTest::newRow(dataTag: "Continuation frame with payload size > INT_MAX" ) |
| 952 | << quint8(FIN | QWebSocketProtocol::OpCodeContinue) |
| 953 | << quint8(127) |
| 954 | << QByteArray(wireRepresentation, 8).append(a: QByteArray(32, 'a')) |
| 955 | << true |
| 956 | << QWebSocketProtocol::CloseCodeTooMuchData; |
| 957 | } |
| 958 | |
| 959 | void tst_DataProcessor::frameTooBig() |
| 960 | { |
| 961 | doTest(); |
| 962 | } |
| 963 | |
| 964 | void tst_DataProcessor::() |
| 965 | { |
| 966 | //The first byte contain the FIN, RSV1, RSV2, RSV3 and the Opcode |
| 967 | //The second byte contains the MaskFlag and the length of the frame |
| 968 | QTest::addColumn<quint8>(name: "firstByte" ); |
| 969 | QTest::addColumn<quint8>(name: "secondByte" ); |
| 970 | //superfluous, but present to be able to call doTest(), which expects a payload field |
| 971 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 972 | QTest::addColumn<bool>(name: "isContinuationFrame" ); |
| 973 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "expectedCloseCode" ); |
| 974 | |
| 975 | //invalid bit fields |
| 976 | invalidField(dataTag: "RSV1 set" , invalidFieldValue: RSV1); |
| 977 | invalidField(dataTag: "RSV2 set" , invalidFieldValue: RSV2); |
| 978 | invalidField(dataTag: "RSV3 set" , invalidFieldValue: RSV3); |
| 979 | invalidField(dataTag: "RSV1 and RSV2 set" , invalidFieldValue: RSV1 | RSV2); |
| 980 | invalidField(dataTag: "RSV1 and RSV3 set" , invalidFieldValue: RSV1 | RSV3); |
| 981 | invalidField(dataTag: "RSV2 and RSV3 set" , invalidFieldValue: RSV2 | RSV3); |
| 982 | invalidField(dataTag: "RSV1, RSV2 and RSV3 set" , invalidFieldValue: RSV1 | RSV2 | RSV3); |
| 983 | |
| 984 | //invalid opcodes |
| 985 | invalidField(dataTag: "Invalid OpCode 3" , invalidFieldValue: QWebSocketProtocol::OpCodeReserved3); |
| 986 | invalidField(dataTag: "Invalid OpCode 4" , invalidFieldValue: QWebSocketProtocol::OpCodeReserved4); |
| 987 | invalidField(dataTag: "Invalid OpCode 5" , invalidFieldValue: QWebSocketProtocol::OpCodeReserved5); |
| 988 | invalidField(dataTag: "Invalid OpCode 6" , invalidFieldValue: QWebSocketProtocol::OpCodeReserved6); |
| 989 | invalidField(dataTag: "Invalid OpCode 7" , invalidFieldValue: QWebSocketProtocol::OpCodeReserved7); |
| 990 | invalidField(dataTag: "Invalid OpCode B" , invalidFieldValue: QWebSocketProtocol::OpCodeReservedB); |
| 991 | invalidField(dataTag: "Invalid OpCode C" , invalidFieldValue: QWebSocketProtocol::OpCodeReservedC); |
| 992 | invalidField(dataTag: "Invalid OpCode D" , invalidFieldValue: QWebSocketProtocol::OpCodeReservedD); |
| 993 | invalidField(dataTag: "Invalid OpCode E" , invalidFieldValue: QWebSocketProtocol::OpCodeReservedE); |
| 994 | invalidField(dataTag: "Invalid OpCode F" , invalidFieldValue: QWebSocketProtocol::OpCodeReservedF); |
| 995 | } |
| 996 | |
| 997 | void tst_DataProcessor::() |
| 998 | { |
| 999 | doTest(); |
| 1000 | } |
| 1001 | |
| 1002 | void tst_DataProcessor::invalidControlFrame_data() |
| 1003 | { |
| 1004 | QTest::addColumn<quint8>(name: "firstByte" ); |
| 1005 | QTest::addColumn<quint8>(name: "secondByte" ); |
| 1006 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 1007 | QTest::addColumn<bool>(name: "isContinuationFrame" ); |
| 1008 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "expectedCloseCode" ); |
| 1009 | |
| 1010 | |
| 1011 | QTest::newRow(dataTag: "Close control frame with payload size 126" ) |
| 1012 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1013 | << quint8(126) |
| 1014 | << QByteArray() |
| 1015 | << false |
| 1016 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1017 | QTest::newRow(dataTag: "Ping control frame with payload size 126" ) |
| 1018 | << quint8(FIN | QWebSocketProtocol::OpCodePing) |
| 1019 | << quint8(126) |
| 1020 | << QByteArray() |
| 1021 | << false |
| 1022 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1023 | QTest::newRow(dataTag: "Close control frame with payload size 126" ) |
| 1024 | << quint8(FIN | QWebSocketProtocol::OpCodePong) |
| 1025 | << quint8(126) |
| 1026 | << QByteArray() |
| 1027 | << false |
| 1028 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1029 | |
| 1030 | QTest::newRow(dataTag: "Non-final close control frame (fragmented)" ) |
| 1031 | << quint8(QWebSocketProtocol::OpCodeClose) |
| 1032 | << quint8(32) |
| 1033 | << QByteArray() |
| 1034 | << false |
| 1035 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1036 | QTest::newRow(dataTag: "Non-final ping control frame (fragmented)" ) |
| 1037 | << quint8(QWebSocketProtocol::OpCodePing) |
| 1038 | << quint8(32) << QByteArray() |
| 1039 | << false |
| 1040 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1041 | QTest::newRow(dataTag: "Non-final pong control frame (fragmented)" ) |
| 1042 | << quint8(QWebSocketProtocol::OpCodePong) |
| 1043 | << quint8(32) |
| 1044 | << QByteArray() |
| 1045 | << false |
| 1046 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1047 | } |
| 1048 | |
| 1049 | void tst_DataProcessor::invalidControlFrame() |
| 1050 | { |
| 1051 | doTest(); |
| 1052 | } |
| 1053 | |
| 1054 | void tst_DataProcessor::invalidCloseFrame_data() |
| 1055 | { |
| 1056 | QTest::addColumn<quint8>(name: "firstByte" ); |
| 1057 | QTest::addColumn<quint8>(name: "secondByte" ); |
| 1058 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 1059 | QTest::addColumn<bool>(name: "isContinuationFrame" ); |
| 1060 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "expectedCloseCode" ); |
| 1061 | |
| 1062 | QTest::newRow(dataTag: "Close control frame with payload size 1" ) |
| 1063 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1064 | << quint8(1) |
| 1065 | << QByteArray(1, 'a') |
| 1066 | << false |
| 1067 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1068 | quint16 swapped = qToBigEndian<quint16>(source: QWebSocketProtocol::CloseCodeAbnormalDisconnection); |
| 1069 | const char *wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1070 | |
| 1071 | //Not allowed per RFC 6455 (see para 7.4.1) |
| 1072 | QTest::newRow(dataTag: "Close control frame close code ABNORMAL DISCONNECTION" ) |
| 1073 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1074 | << quint8(2) |
| 1075 | << QByteArray(wireRepresentation, 2) |
| 1076 | << false |
| 1077 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1078 | swapped = qToBigEndian<quint16>(source: QWebSocketProtocol::CloseCodeMissingStatusCode); |
| 1079 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1080 | //Not allowed per RFC 6455 (see para 7.4.1) |
| 1081 | QTest::newRow(dataTag: "Close control frame close code MISSING STATUS CODE" ) |
| 1082 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1083 | << quint8(2) |
| 1084 | << QByteArray(wireRepresentation, 2) |
| 1085 | << false |
| 1086 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1087 | swapped = qToBigEndian<quint16>(source: 1004); |
| 1088 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1089 | QTest::newRow(dataTag: "Close control frame close code 1004" ) |
| 1090 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1091 | << quint8(2) |
| 1092 | << QByteArray(wireRepresentation, 2) |
| 1093 | << false |
| 1094 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1095 | swapped = qToBigEndian<quint16>(source: QWebSocketProtocol::CloseCodeTlsHandshakeFailed); |
| 1096 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1097 | //Not allowed per RFC 6455 (see para 7.4.1) |
| 1098 | QTest::newRow(dataTag: "Close control frame close code TLS HANDSHAKE FAILED" ) |
| 1099 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1100 | << quint8(2) |
| 1101 | << QByteArray(wireRepresentation, 2) |
| 1102 | << false |
| 1103 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1104 | swapped = qToBigEndian<quint16>(source: 0); |
| 1105 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1106 | QTest::newRow(dataTag: "Close control frame close code 0" ) |
| 1107 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1108 | << quint8(2) |
| 1109 | << QByteArray(wireRepresentation, 2) |
| 1110 | << false |
| 1111 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1112 | swapped = qToBigEndian<quint16>(source: 999); |
| 1113 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1114 | QTest::newRow(dataTag: "Close control frame close code 999" ) |
| 1115 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1116 | << quint8(2) |
| 1117 | << QByteArray(wireRepresentation, 2) |
| 1118 | << false |
| 1119 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1120 | swapped = qToBigEndian<quint16>(source: 1012); |
| 1121 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1122 | QTest::newRow(dataTag: "Close control frame close code 1012" ) |
| 1123 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1124 | << quint8(2) |
| 1125 | << QByteArray(wireRepresentation, 2) |
| 1126 | << false |
| 1127 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1128 | swapped = qToBigEndian<quint16>(source: 1013); |
| 1129 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1130 | QTest::newRow(dataTag: "Close control frame close code 1013" ) |
| 1131 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1132 | << quint8(2) |
| 1133 | << QByteArray(wireRepresentation, 2) |
| 1134 | << false |
| 1135 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1136 | swapped = qToBigEndian<quint16>(source: 1014); |
| 1137 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1138 | QTest::newRow(dataTag: "Close control frame close code 1014" ) |
| 1139 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1140 | << quint8(2) |
| 1141 | << QByteArray(wireRepresentation, 2) |
| 1142 | << false |
| 1143 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1144 | swapped = qToBigEndian<quint16>(source: 1100); |
| 1145 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1146 | QTest::newRow(dataTag: "Close control frame close code 1100" ) |
| 1147 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1148 | << quint8(2) |
| 1149 | << QByteArray(wireRepresentation, 2) |
| 1150 | << false |
| 1151 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1152 | swapped = qToBigEndian<quint16>(source: 2000); |
| 1153 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1154 | QTest::newRow(dataTag: "Close control frame close code 2000" ) |
| 1155 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1156 | << quint8(2) |
| 1157 | << QByteArray(wireRepresentation, 2) |
| 1158 | << false |
| 1159 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1160 | swapped = qToBigEndian<quint16>(source: 2999); |
| 1161 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1162 | QTest::newRow(dataTag: "Close control frame close code 2999" ) |
| 1163 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1164 | << quint8(2) |
| 1165 | << QByteArray(wireRepresentation, 2) |
| 1166 | << false |
| 1167 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1168 | swapped = qToBigEndian<quint16>(source: 5000); |
| 1169 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1170 | QTest::newRow(dataTag: "Close control frame close code 5000" ) |
| 1171 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1172 | << quint8(2) |
| 1173 | << QByteArray(wireRepresentation, 2) |
| 1174 | << false |
| 1175 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1176 | swapped = qToBigEndian<quint16>(source: 65535u); |
| 1177 | wireRepresentation = static_cast<const char *>(static_cast<const void *>(&swapped)); |
| 1178 | QTest::newRow(dataTag: "Close control frame close code 65535" ) |
| 1179 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1180 | << quint8(2) |
| 1181 | << QByteArray(wireRepresentation, 2) |
| 1182 | << false |
| 1183 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1184 | } |
| 1185 | |
| 1186 | void tst_DataProcessor::invalidCloseFrame() |
| 1187 | { |
| 1188 | doCloseFrameTest(); |
| 1189 | } |
| 1190 | |
| 1191 | void tst_DataProcessor::minimumSizeRequirement_data() |
| 1192 | { |
| 1193 | QTest::addColumn<quint8>(name: "firstByte" ); |
| 1194 | QTest::addColumn<quint8>(name: "secondByte" ); |
| 1195 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 1196 | QTest::addColumn<bool>(name: "isContinuationFrame" ); |
| 1197 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "expectedCloseCode" ); |
| 1198 | |
| 1199 | minimumSize16Bit(sizeInBytes: 0); |
| 1200 | minimumSize16Bit(sizeInBytes: 64); |
| 1201 | minimumSize16Bit(sizeInBytes: 125); |
| 1202 | |
| 1203 | minimumSize64Bit(sizeInBytes: 0); |
| 1204 | minimumSize64Bit(sizeInBytes: 64); |
| 1205 | minimumSize64Bit(sizeInBytes: 125); |
| 1206 | minimumSize64Bit(sizeInBytes: 126); |
| 1207 | minimumSize64Bit(sizeInBytes: 256); |
| 1208 | minimumSize64Bit(sizeInBytes: 512); |
| 1209 | minimumSize64Bit(sizeInBytes: 1024); |
| 1210 | minimumSize64Bit(sizeInBytes: 2048); |
| 1211 | minimumSize64Bit(sizeInBytes: 4096); |
| 1212 | minimumSize64Bit(sizeInBytes: 8192); |
| 1213 | minimumSize64Bit(sizeInBytes: 16384); |
| 1214 | minimumSize64Bit(sizeInBytes: 32768); |
| 1215 | minimumSize64Bit(sizeInBytes: 0xFFFFu); |
| 1216 | } |
| 1217 | |
| 1218 | void tst_DataProcessor::minimumSizeRequirement() |
| 1219 | { |
| 1220 | doTest(); |
| 1221 | } |
| 1222 | |
| 1223 | void tst_DataProcessor::invalidPayload_data(bool isControlFrame) |
| 1224 | { |
| 1225 | QTest::addColumn<quint8>(name: "firstByte" ); |
| 1226 | QTest::addColumn<quint8>(name: "secondByte" ); |
| 1227 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 1228 | QTest::addColumn<bool>(name: "isContinuationFrame" ); |
| 1229 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "expectedCloseCode" ); |
| 1230 | |
| 1231 | //6.3: invalid UTF-8 sequence |
| 1232 | invalidUTF8(dataTag: "case 6.3.1" , utf8Sequence: "cebae1bdb9cf83cebcceb5eda080656469746564" , isCloseFrame: isControlFrame); |
| 1233 | |
| 1234 | //6.4.: fail fast tests; invalid UTF-8 in middle of string |
| 1235 | invalidUTF8(dataTag: "case 6.4.1" , utf8Sequence: "cebae1bdb9cf83cebcceb5f4908080656469746564" , isCloseFrame: isControlFrame); |
| 1236 | invalidUTF8(dataTag: "case 6.4.4" , utf8Sequence: "cebae1bdb9cf83cebcceb5eda080656469746564" , isCloseFrame: isControlFrame); |
| 1237 | |
| 1238 | //6.6: All prefixes of a valid UTF-8 string that contains multi-byte code points |
| 1239 | invalidUTF8(dataTag: "case 6.6.1" , utf8Sequence: "ce" , isCloseFrame: isControlFrame); |
| 1240 | invalidUTF8(dataTag: "case 6.6.3" , utf8Sequence: "cebae1" , isCloseFrame: isControlFrame); |
| 1241 | invalidUTF8(dataTag: "case 6.6.4" , utf8Sequence: "cebae1bd" , isCloseFrame: isControlFrame); |
| 1242 | invalidUTF8(dataTag: "case 6.6.6" , utf8Sequence: "cebae1bdb9cf" , isCloseFrame: isControlFrame); |
| 1243 | invalidUTF8(dataTag: "case 6.6.8" , utf8Sequence: "cebae1bdb9cf83ce" , isCloseFrame: isControlFrame); |
| 1244 | invalidUTF8(dataTag: "case 6.6.10" , utf8Sequence: "cebae1bdb9cf83cebcce" , isCloseFrame: isControlFrame); |
| 1245 | |
| 1246 | //6.8: First possible sequence length 5/6 (invalid codepoints) |
| 1247 | invalidUTF8(dataTag: "case 6.8.1" , utf8Sequence: "f888808080" , isCloseFrame: isControlFrame); |
| 1248 | invalidUTF8(dataTag: "case 6.8.2" , utf8Sequence: "fc8480808080" , isCloseFrame: isControlFrame); |
| 1249 | |
| 1250 | //6.10: Last possible sequence length 4/5/6 (invalid codepoints) |
| 1251 | invalidUTF8(dataTag: "case 6.10.1" , utf8Sequence: "f7bfbfbf" , isCloseFrame: isControlFrame); |
| 1252 | invalidUTF8(dataTag: "case 6.10.2" , utf8Sequence: "fbbfbfbfbf" , isCloseFrame: isControlFrame); |
| 1253 | invalidUTF8(dataTag: "case 6.10.3" , utf8Sequence: "fdbfbfbfbfbf" , isCloseFrame: isControlFrame); |
| 1254 | |
| 1255 | //5.11: boundary conditions |
| 1256 | invalidUTF8(dataTag: "case 6.11.5" , utf8Sequence: "f4908080" , isCloseFrame: isControlFrame); |
| 1257 | |
| 1258 | //6.12: unexpected continuation bytes |
| 1259 | invalidUTF8(dataTag: "case 6.12.1" , utf8Sequence: "80" , isCloseFrame: isControlFrame); |
| 1260 | invalidUTF8(dataTag: "case 6.12.2" , utf8Sequence: "bf" , isCloseFrame: isControlFrame); |
| 1261 | invalidUTF8(dataTag: "case 6.12.3" , utf8Sequence: "80bf" , isCloseFrame: isControlFrame); |
| 1262 | invalidUTF8(dataTag: "case 6.12.4" , utf8Sequence: "80bf80" , isCloseFrame: isControlFrame); |
| 1263 | invalidUTF8(dataTag: "case 6.12.5" , utf8Sequence: "80bf80bf" , isCloseFrame: isControlFrame); |
| 1264 | invalidUTF8(dataTag: "case 6.12.6" , utf8Sequence: "80bf80bf80" , isCloseFrame: isControlFrame); |
| 1265 | invalidUTF8(dataTag: "case 6.12.7" , utf8Sequence: "80bf80bf80bf" , isCloseFrame: isControlFrame); |
| 1266 | invalidUTF8(dataTag: "case 6.12.8" , |
| 1267 | utf8Sequence: "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a" |
| 1268 | "7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbe" , |
| 1269 | isCloseFrame: isControlFrame); |
| 1270 | |
| 1271 | //6.13: lonely start characters |
| 1272 | invalidUTF8(dataTag: "case 6.13.1" , |
| 1273 | utf8Sequence: "c020c120c220c320c420c520c620c720c820c920ca20cb20cc20cd20ce20cf20d020d120d220" |
| 1274 | "d320d420d520d620d720d820d920da20db20dc20dd20de20" , |
| 1275 | isCloseFrame: isControlFrame); |
| 1276 | invalidUTF8(dataTag: "case 6.13.2" , utf8Sequence: "e020e120e220e320e420e520e620e720e820e920ea20eb20ec20ed20ee20" , |
| 1277 | isCloseFrame: isControlFrame); |
| 1278 | invalidUTF8(dataTag: "case 6.13.3" , utf8Sequence: "f020f120f220f320f420f520f620" , isCloseFrame: isControlFrame); |
| 1279 | invalidUTF8(dataTag: "case 6.13.4" , utf8Sequence: "f820f920fa20" , isCloseFrame: isControlFrame); |
| 1280 | invalidUTF8(dataTag: "case 6.13.5" , utf8Sequence: "fc20" , isCloseFrame: isControlFrame); |
| 1281 | |
| 1282 | //6.14: sequences with last continuation byte missing |
| 1283 | invalidUTF8(dataTag: "case 6.14.1" , utf8Sequence: "c0" , isCloseFrame: isControlFrame); |
| 1284 | invalidUTF8(dataTag: "case 6.14.2" , utf8Sequence: "e080" , isCloseFrame: isControlFrame); |
| 1285 | invalidUTF8(dataTag: "case 6.14.3" , utf8Sequence: "f08080" , isCloseFrame: isControlFrame); |
| 1286 | invalidUTF8(dataTag: "case 6.14.4" , utf8Sequence: "f8808080" , isCloseFrame: isControlFrame); |
| 1287 | invalidUTF8(dataTag: "case 6.14.5" , utf8Sequence: "fc80808080" , isCloseFrame: isControlFrame); |
| 1288 | invalidUTF8(dataTag: "case 6.14.6" , utf8Sequence: "df" , isCloseFrame: isControlFrame); |
| 1289 | invalidUTF8(dataTag: "case 6.14.7" , utf8Sequence: "efbf" , isCloseFrame: isControlFrame); |
| 1290 | invalidUTF8(dataTag: "case 6.14.8" , utf8Sequence: "f7bfbf" , isCloseFrame: isControlFrame); |
| 1291 | invalidUTF8(dataTag: "case 6.14.9" , utf8Sequence: "fbbfbfbf" , isCloseFrame: isControlFrame); |
| 1292 | invalidUTF8(dataTag: "case 6.14.10" , utf8Sequence: "fdbfbfbfbf" , isCloseFrame: isControlFrame); |
| 1293 | |
| 1294 | //6.15: concatenation of incomplete sequences |
| 1295 | invalidUTF8(dataTag: "case 6.15.1" , |
| 1296 | utf8Sequence: "c0e080f08080f8808080fc80808080dfefbff7bfbffbbfbfbffdbfbfbfbf" , isCloseFrame: isControlFrame); |
| 1297 | |
| 1298 | //6.16: impossible bytes |
| 1299 | invalidUTF8(dataTag: "case 6.16.1" , utf8Sequence: "fe" , isCloseFrame: isControlFrame); |
| 1300 | invalidUTF8(dataTag: "case 6.16.2" , utf8Sequence: "ff" , isCloseFrame: isControlFrame); |
| 1301 | invalidUTF8(dataTag: "case 6.16.3" , utf8Sequence: "fefeffff" , isCloseFrame: isControlFrame); |
| 1302 | |
| 1303 | //6.17: overlong ASCII characters |
| 1304 | invalidUTF8(dataTag: "case 6.17.1" , utf8Sequence: "c0af" , isCloseFrame: isControlFrame); |
| 1305 | invalidUTF8(dataTag: "case 6.17.2" , utf8Sequence: "e080af" , isCloseFrame: isControlFrame); |
| 1306 | invalidUTF8(dataTag: "case 6.17.3" , utf8Sequence: "f08080af" , isCloseFrame: isControlFrame); |
| 1307 | invalidUTF8(dataTag: "case 6.17.4" , utf8Sequence: "f8808080af" , isCloseFrame: isControlFrame); |
| 1308 | invalidUTF8(dataTag: "case 6.17.5" , utf8Sequence: "fc80808080af" , isCloseFrame: isControlFrame); |
| 1309 | |
| 1310 | //6.18: maximum overlong sequences |
| 1311 | invalidUTF8(dataTag: "case 6.18.1" , utf8Sequence: "c1bf" , isCloseFrame: isControlFrame); |
| 1312 | invalidUTF8(dataTag: "case 6.18.2" , utf8Sequence: "e09fbf" , isCloseFrame: isControlFrame); |
| 1313 | invalidUTF8(dataTag: "case 6.18.3" , utf8Sequence: "f08fbfbf" , isCloseFrame: isControlFrame); |
| 1314 | invalidUTF8(dataTag: "case 6.18.4" , utf8Sequence: "f887bfbfbf" , isCloseFrame: isControlFrame); |
| 1315 | invalidUTF8(dataTag: "case 6.18.5" , utf8Sequence: "fc83bfbfbfbf" , isCloseFrame: isControlFrame); |
| 1316 | |
| 1317 | //6.19: overlong presentation of the NUL character |
| 1318 | invalidUTF8(dataTag: "case 6.19.1" , utf8Sequence: "c080" , isCloseFrame: isControlFrame); |
| 1319 | invalidUTF8(dataTag: "case 6.19.2" , utf8Sequence: "e08080" , isCloseFrame: isControlFrame); |
| 1320 | invalidUTF8(dataTag: "case 6.19.3" , utf8Sequence: "f0808080" , isCloseFrame: isControlFrame); |
| 1321 | invalidUTF8(dataTag: "case 6.19.4" , utf8Sequence: "f880808080" , isCloseFrame: isControlFrame); |
| 1322 | invalidUTF8(dataTag: "case 6.19.5" , utf8Sequence: "fc8080808080" , isCloseFrame: isControlFrame); |
| 1323 | |
| 1324 | //6.20: Single UTF-16 surrogates |
| 1325 | invalidUTF8(dataTag: "case 6.20.1" , utf8Sequence: "eda080" , isCloseFrame: isControlFrame); |
| 1326 | invalidUTF8(dataTag: "case 6.20.2" , utf8Sequence: "edadbf" , isCloseFrame: isControlFrame); |
| 1327 | invalidUTF8(dataTag: "case 6.20.3" , utf8Sequence: "edae80" , isCloseFrame: isControlFrame); |
| 1328 | invalidUTF8(dataTag: "case 6.20.4" , utf8Sequence: "edafbf" , isCloseFrame: isControlFrame); |
| 1329 | invalidUTF8(dataTag: "case 6.20.5" , utf8Sequence: "edb080" , isCloseFrame: isControlFrame); |
| 1330 | invalidUTF8(dataTag: "case 6.20.6" , utf8Sequence: "edbe80" , isCloseFrame: isControlFrame); |
| 1331 | invalidUTF8(dataTag: "case 6.20.7" , utf8Sequence: "edbfbf" , isCloseFrame: isControlFrame); |
| 1332 | |
| 1333 | //6.21: Paired UTF-16 surrogates |
| 1334 | invalidUTF8(dataTag: "case 6.21.1" , utf8Sequence: "eda080edb080" , isCloseFrame: isControlFrame); |
| 1335 | invalidUTF8(dataTag: "case 6.21.2" , utf8Sequence: "eda080edbfbf" , isCloseFrame: isControlFrame); |
| 1336 | invalidUTF8(dataTag: "case 6.21.3" , utf8Sequence: "edadbfedb080" , isCloseFrame: isControlFrame); |
| 1337 | invalidUTF8(dataTag: "case 6.21.4" , utf8Sequence: "edadbfedbfbf" , isCloseFrame: isControlFrame); |
| 1338 | invalidUTF8(dataTag: "case 6.21.5" , utf8Sequence: "edae80edb080" , isCloseFrame: isControlFrame); |
| 1339 | invalidUTF8(dataTag: "case 6.21.6" , utf8Sequence: "edae80edbfbf" , isCloseFrame: isControlFrame); |
| 1340 | invalidUTF8(dataTag: "case 6.21.7" , utf8Sequence: "edafbfedb080" , isCloseFrame: isControlFrame); |
| 1341 | invalidUTF8(dataTag: "case 6.21.8" , utf8Sequence: "edafbfedbfbf" , isCloseFrame: isControlFrame); |
| 1342 | } |
| 1343 | |
| 1344 | void tst_DataProcessor::invalidPayload() |
| 1345 | { |
| 1346 | doTest(); |
| 1347 | } |
| 1348 | |
| 1349 | void tst_DataProcessor::invalidPayloadInCloseFrame_data() |
| 1350 | { |
| 1351 | invalidPayload_data(isControlFrame: true); |
| 1352 | } |
| 1353 | |
| 1354 | void tst_DataProcessor::invalidPayloadInCloseFrame() |
| 1355 | { |
| 1356 | QFETCH(quint8, firstByte); |
| 1357 | QFETCH(quint8, secondByte); |
| 1358 | QFETCH(QByteArray, payload); |
| 1359 | QFETCH(bool, isContinuationFrame); |
| 1360 | QFETCH(QWebSocketProtocol::CloseCode, expectedCloseCode); |
| 1361 | |
| 1362 | Q_UNUSED(isContinuationFrame) |
| 1363 | |
| 1364 | QByteArray data; |
| 1365 | QBuffer buffer; |
| 1366 | QWebSocketDataProcessor dataProcessor; |
| 1367 | QSignalSpy closeSpy(&dataProcessor, |
| 1368 | SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); |
| 1369 | QSignalSpy errorSpy(&dataProcessor, |
| 1370 | SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); |
| 1371 | QSignalSpy pingMessageSpy(&dataProcessor, SIGNAL(pingReceived(QByteArray))); |
| 1372 | QSignalSpy pongMessageSpy(&dataProcessor, SIGNAL(pongReceived(QByteArray))); |
| 1373 | QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString))); |
| 1374 | QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); |
| 1375 | QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); |
| 1376 | QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); |
| 1377 | |
| 1378 | data.append(c: firstByte).append(c: secondByte); |
| 1379 | data.append(a: payload); |
| 1380 | buffer.setData(data); |
| 1381 | buffer.open(openMode: QIODevice::ReadOnly); |
| 1382 | dataProcessor.process(pIoDevice: &buffer); |
| 1383 | QCOMPARE(closeSpy.count(), 1); |
| 1384 | QCOMPARE(errorSpy.count(), 0); |
| 1385 | QCOMPARE(pingMessageSpy.count(), 0); |
| 1386 | QCOMPARE(pongMessageSpy.count(), 0); |
| 1387 | QCOMPARE(textMessageSpy.count(), 0); |
| 1388 | QCOMPARE(binaryMessageSpy.count(), 0); |
| 1389 | QCOMPARE(textFrameSpy.count(), 0); |
| 1390 | QCOMPARE(binaryFrameSpy.count(), 0); |
| 1391 | QVariantList arguments = closeSpy.takeFirst(); |
| 1392 | QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode); |
| 1393 | buffer.close(); |
| 1394 | } |
| 1395 | |
| 1396 | void tst_DataProcessor::incompletePayload_data() |
| 1397 | { |
| 1398 | QTest::addColumn<quint8>(name: "firstByte" ); |
| 1399 | QTest::addColumn<quint8>(name: "secondByte" ); |
| 1400 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 1401 | QTest::addColumn<bool>(name: "isContinuationFrame" ); |
| 1402 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "expectedCloseCode" ); |
| 1403 | |
| 1404 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeText, indicatedSize: 125, actualPayloadSize: 0); |
| 1405 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeText, indicatedSize: 64, actualPayloadSize: 32); |
| 1406 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeText, indicatedSize: 256, actualPayloadSize: 32); |
| 1407 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeText, indicatedSize: 128000, actualPayloadSize: 32); |
| 1408 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeBinary, indicatedSize: 125, actualPayloadSize: 0); |
| 1409 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeBinary, indicatedSize: 64, actualPayloadSize: 32); |
| 1410 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeBinary, indicatedSize: 256, actualPayloadSize: 32); |
| 1411 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeBinary, indicatedSize: 128000, actualPayloadSize: 32); |
| 1412 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeContinue, indicatedSize: 125, actualPayloadSize: 0); |
| 1413 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeContinue, indicatedSize: 64, actualPayloadSize: 32); |
| 1414 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeContinue, indicatedSize: 256, actualPayloadSize: 32); |
| 1415 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeContinue, indicatedSize: 128000, actualPayloadSize: 32); |
| 1416 | |
| 1417 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodeClose, indicatedSize: 64, actualPayloadSize: 32); |
| 1418 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodePing, indicatedSize: 64, actualPayloadSize: 32); |
| 1419 | incompleteFrame(controlCode: QWebSocketProtocol::OpCodePong, indicatedSize: 64, actualPayloadSize: 32); |
| 1420 | } |
| 1421 | |
| 1422 | void tst_DataProcessor::incompletePayload() |
| 1423 | { |
| 1424 | doTest(timeout: 7000); |
| 1425 | } |
| 1426 | |
| 1427 | void tst_DataProcessor::incompleteSizeField_data() |
| 1428 | { |
| 1429 | QTest::addColumn<quint8>(name: "firstByte" ); |
| 1430 | QTest::addColumn<quint8>(name: "secondByte" ); |
| 1431 | QTest::addColumn<QByteArray>(name: "payload" ); |
| 1432 | QTest::addColumn<bool>(name: "isContinuationFrame" ); |
| 1433 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "expectedCloseCode" ); |
| 1434 | |
| 1435 | //for a frame length value of 126 |
| 1436 | //there should be 2 bytes following to form a 16-bit frame length |
| 1437 | insertIncompleteSizeFieldTest(payloadCode: 126, numBytesFollowing: 0); |
| 1438 | insertIncompleteSizeFieldTest(payloadCode: 126, numBytesFollowing: 1); |
| 1439 | |
| 1440 | //for a frame length value of 127 |
| 1441 | //there should be 8 bytes following to form a 64-bit frame length |
| 1442 | insertIncompleteSizeFieldTest(payloadCode: 127, numBytesFollowing: 0); |
| 1443 | insertIncompleteSizeFieldTest(payloadCode: 127, numBytesFollowing: 1); |
| 1444 | insertIncompleteSizeFieldTest(payloadCode: 127, numBytesFollowing: 2); |
| 1445 | insertIncompleteSizeFieldTest(payloadCode: 127, numBytesFollowing: 3); |
| 1446 | insertIncompleteSizeFieldTest(payloadCode: 127, numBytesFollowing: 4); |
| 1447 | insertIncompleteSizeFieldTest(payloadCode: 127, numBytesFollowing: 5); |
| 1448 | insertIncompleteSizeFieldTest(payloadCode: 127, numBytesFollowing: 6); |
| 1449 | insertIncompleteSizeFieldTest(payloadCode: 127, numBytesFollowing: 7); |
| 1450 | } |
| 1451 | |
| 1452 | void tst_DataProcessor::incompleteSizeField() |
| 1453 | { |
| 1454 | doTest(timeout: 7000); |
| 1455 | } |
| 1456 | |
| 1457 | ////////////////////////////////////////////////////////////////////////////////////////// |
| 1458 | /// HELPER FUNCTIONS |
| 1459 | ////////////////////////////////////////////////////////////////////////////////////////// |
| 1460 | void tst_DataProcessor::doTest(int timeout) |
| 1461 | { |
| 1462 | QFETCH(quint8, firstByte); |
| 1463 | QFETCH(quint8, secondByte); |
| 1464 | QFETCH(QByteArray, payload); |
| 1465 | QFETCH(bool, isContinuationFrame); |
| 1466 | QFETCH(QWebSocketProtocol::CloseCode, expectedCloseCode); |
| 1467 | |
| 1468 | QByteArray data; |
| 1469 | QBuffer buffer; |
| 1470 | QWebSocketDataProcessor dataProcessor; |
| 1471 | QSignalSpy errorSpy(&dataProcessor, |
| 1472 | SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); |
| 1473 | QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString))); |
| 1474 | QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); |
| 1475 | QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); |
| 1476 | QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); |
| 1477 | |
| 1478 | if (isContinuationFrame) |
| 1479 | { |
| 1480 | data.append(c: quint8(QWebSocketProtocol::OpCodeText)) |
| 1481 | .append(c: char(1)) |
| 1482 | .append(a: QByteArray(1, 'a')); |
| 1483 | } |
| 1484 | data.append(c: firstByte).append(c: secondByte); |
| 1485 | data.append(a: payload); |
| 1486 | buffer.setData(data); |
| 1487 | buffer.open(openMode: QIODevice::ReadOnly); |
| 1488 | dataProcessor.process(pIoDevice: &buffer); |
| 1489 | QTRY_VERIFY_WITH_TIMEOUT(errorSpy.count(), timeout); |
| 1490 | QCOMPARE(errorSpy.count(), 1); |
| 1491 | QCOMPARE(textMessageSpy.count(), 0); |
| 1492 | QCOMPARE(binaryMessageSpy.count(), 0); |
| 1493 | QCOMPARE(textFrameSpy.count(), isContinuationFrame ? 1 : 0); |
| 1494 | QCOMPARE(binaryFrameSpy.count(), 0); |
| 1495 | QVariantList arguments = errorSpy.takeFirst(); |
| 1496 | QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode); |
| 1497 | buffer.close(); |
| 1498 | errorSpy.clear(); |
| 1499 | data.clear(); |
| 1500 | } |
| 1501 | |
| 1502 | void tst_DataProcessor::doCloseFrameTest() |
| 1503 | { |
| 1504 | QFETCH(quint8, firstByte); |
| 1505 | QFETCH(quint8, secondByte); |
| 1506 | QFETCH(QByteArray, payload); |
| 1507 | QFETCH(bool, isContinuationFrame); |
| 1508 | QFETCH(QWebSocketProtocol::CloseCode, expectedCloseCode); |
| 1509 | |
| 1510 | Q_UNUSED(isContinuationFrame) |
| 1511 | |
| 1512 | QByteArray data; |
| 1513 | QBuffer buffer; |
| 1514 | QWebSocketDataProcessor dataProcessor; |
| 1515 | QSignalSpy closeSpy(&dataProcessor, |
| 1516 | SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString))); |
| 1517 | QSignalSpy errorSpy(&dataProcessor, |
| 1518 | SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString))); |
| 1519 | QSignalSpy textMessageSpy(&dataProcessor, SIGNAL(textMessageReceived(QString))); |
| 1520 | QSignalSpy binaryMessageSpy(&dataProcessor, SIGNAL(binaryMessageReceived(QByteArray))); |
| 1521 | QSignalSpy textFrameSpy(&dataProcessor, SIGNAL(textFrameReceived(QString,bool))); |
| 1522 | QSignalSpy binaryFrameSpy(&dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool))); |
| 1523 | |
| 1524 | data.append(c: firstByte).append(c: secondByte); |
| 1525 | data.append(a: payload); |
| 1526 | buffer.setData(data); |
| 1527 | buffer.open(openMode: QIODevice::ReadOnly); |
| 1528 | dataProcessor.process(pIoDevice: &buffer); |
| 1529 | QCOMPARE(closeSpy.count(), 1); |
| 1530 | QCOMPARE(errorSpy.count(), 0); |
| 1531 | QCOMPARE(textMessageSpy.count(), 0); |
| 1532 | QCOMPARE(binaryMessageSpy.count(), 0); |
| 1533 | QCOMPARE(textFrameSpy.count(), 0); |
| 1534 | QCOMPARE(binaryFrameSpy.count(), 0); |
| 1535 | QVariantList arguments = closeSpy.takeFirst(); |
| 1536 | QCOMPARE(arguments.at(0).value<QWebSocketProtocol::CloseCode>(), expectedCloseCode); |
| 1537 | buffer.close(); |
| 1538 | } |
| 1539 | |
| 1540 | QString tst_DataProcessor::opCodeToString(quint8 opCode) |
| 1541 | { |
| 1542 | QString frameType; |
| 1543 | switch (opCode) |
| 1544 | { |
| 1545 | case QWebSocketProtocol::OpCodeBinary: |
| 1546 | { |
| 1547 | frameType = QStringLiteral("Binary" ); |
| 1548 | break; |
| 1549 | } |
| 1550 | case QWebSocketProtocol::OpCodeText: |
| 1551 | { |
| 1552 | frameType = QStringLiteral("Text" ); |
| 1553 | break; |
| 1554 | } |
| 1555 | case QWebSocketProtocol::OpCodePing: |
| 1556 | { |
| 1557 | frameType = QStringLiteral("Ping" ); |
| 1558 | break; |
| 1559 | } |
| 1560 | case QWebSocketProtocol::OpCodePong: |
| 1561 | { |
| 1562 | frameType = QStringLiteral("Pong" ); |
| 1563 | break; |
| 1564 | } |
| 1565 | case QWebSocketProtocol::OpCodeClose: |
| 1566 | { |
| 1567 | frameType = QStringLiteral("Close" ); |
| 1568 | break; |
| 1569 | } |
| 1570 | case QWebSocketProtocol::OpCodeContinue: |
| 1571 | { |
| 1572 | frameType = QStringLiteral("Continuation" ); |
| 1573 | break; |
| 1574 | } |
| 1575 | case QWebSocketProtocol::OpCodeReserved3: |
| 1576 | { |
| 1577 | frameType = QStringLiteral("Reserved3" ); |
| 1578 | break; |
| 1579 | } |
| 1580 | case QWebSocketProtocol::OpCodeReserved4: |
| 1581 | { |
| 1582 | frameType = QStringLiteral("Reserved5" ); |
| 1583 | break; |
| 1584 | } |
| 1585 | case QWebSocketProtocol::OpCodeReserved5: |
| 1586 | { |
| 1587 | frameType = QStringLiteral("Reserved5" ); |
| 1588 | break; |
| 1589 | } |
| 1590 | case QWebSocketProtocol::OpCodeReserved6: |
| 1591 | { |
| 1592 | frameType = QStringLiteral("Reserved6" ); |
| 1593 | break; |
| 1594 | } |
| 1595 | case QWebSocketProtocol::OpCodeReserved7: |
| 1596 | { |
| 1597 | frameType = QStringLiteral("Reserved7" ); |
| 1598 | break; |
| 1599 | } |
| 1600 | case QWebSocketProtocol::OpCodeReservedB: |
| 1601 | { |
| 1602 | frameType = QStringLiteral("ReservedB" ); |
| 1603 | break; |
| 1604 | } |
| 1605 | case QWebSocketProtocol::OpCodeReservedC: |
| 1606 | { |
| 1607 | frameType = QStringLiteral("ReservedC" ); |
| 1608 | break; |
| 1609 | } |
| 1610 | case QWebSocketProtocol::OpCodeReservedD: |
| 1611 | { |
| 1612 | frameType = QStringLiteral("ReservedD" ); |
| 1613 | break; |
| 1614 | } |
| 1615 | case QWebSocketProtocol::OpCodeReservedE: |
| 1616 | { |
| 1617 | frameType = QStringLiteral("ReservedE" ); |
| 1618 | break; |
| 1619 | } |
| 1620 | case QWebSocketProtocol::OpCodeReservedF: |
| 1621 | { |
| 1622 | frameType = QStringLiteral("ReservedF" ); |
| 1623 | break; |
| 1624 | } |
| 1625 | default: |
| 1626 | { |
| 1627 | //should never come here |
| 1628 | Q_ASSERT(false); |
| 1629 | } |
| 1630 | } |
| 1631 | return frameType; |
| 1632 | } |
| 1633 | |
| 1634 | void tst_DataProcessor::minimumSize16Bit(quint16 sizeInBytes) |
| 1635 | { |
| 1636 | quint16 swapped16 = qToBigEndian<quint16>(source: sizeInBytes); |
| 1637 | const char *wireRepresentation |
| 1638 | = static_cast<const char *>(static_cast<const void *>(&swapped16)); |
| 1639 | QTest::newRow(QStringLiteral("Text frame with payload size %1, represented in 2 bytes" ) |
| 1640 | .arg(a: sizeInBytes).toLatin1().constData()) |
| 1641 | << quint8(FIN | QWebSocketProtocol::OpCodeText) |
| 1642 | << quint8(126) |
| 1643 | << QByteArray(wireRepresentation, 2) |
| 1644 | << false |
| 1645 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1646 | QTest::newRow(QStringLiteral("Binary frame with payload size %1, represented in 2 bytes" ) |
| 1647 | .arg(a: sizeInBytes).toLatin1().constBegin()) |
| 1648 | << quint8(FIN | QWebSocketProtocol::OpCodeBinary) |
| 1649 | << quint8(126) |
| 1650 | << QByteArray(wireRepresentation, 2) |
| 1651 | << false |
| 1652 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1653 | QTest::newRow(QStringLiteral("Continuation frame with payload size %1, represented in 2 bytes" ) |
| 1654 | .arg(a: sizeInBytes).toLatin1().constData()) |
| 1655 | << quint8(FIN | QWebSocketProtocol::OpCodeContinue) |
| 1656 | << quint8(126) |
| 1657 | << QByteArray(wireRepresentation, 2) |
| 1658 | << true |
| 1659 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1660 | } |
| 1661 | |
| 1662 | void tst_DataProcessor::minimumSize64Bit(quint64 sizeInBytes) |
| 1663 | { |
| 1664 | quint64 swapped64 = qToBigEndian<quint64>(source: sizeInBytes); |
| 1665 | const char *wireRepresentation |
| 1666 | = static_cast<const char *>(static_cast<const void *>(&swapped64)); |
| 1667 | |
| 1668 | QTest::newRow(QStringLiteral("Text frame with payload size %1, represented in 8 bytes" ) |
| 1669 | .arg(a: sizeInBytes).toLatin1().constData()) |
| 1670 | << quint8(FIN | QWebSocketProtocol::OpCodeText) |
| 1671 | << quint8(127) |
| 1672 | << QByteArray(wireRepresentation, 8) |
| 1673 | << false |
| 1674 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1675 | |
| 1676 | QTest::newRow(QStringLiteral("Binary frame with payload size %1, represented in 8 bytes" ) |
| 1677 | .arg(a: sizeInBytes).toLatin1().constData()) |
| 1678 | << quint8(FIN | QWebSocketProtocol::OpCodeBinary) |
| 1679 | << quint8(127) |
| 1680 | << QByteArray(wireRepresentation, 8) |
| 1681 | << false |
| 1682 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1683 | |
| 1684 | QTest::newRow(QStringLiteral("Continuation frame with payload size %1, represented in 8 bytes" ) |
| 1685 | .arg(a: sizeInBytes).toLatin1().constData()) |
| 1686 | << quint8(FIN | QWebSocketProtocol::OpCodeContinue) |
| 1687 | << quint8(127) |
| 1688 | << QByteArray(wireRepresentation, 8) |
| 1689 | << true |
| 1690 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1691 | } |
| 1692 | |
| 1693 | void tst_DataProcessor::invalidUTF8(const char *dataTag, const char *utf8Sequence, |
| 1694 | bool isCloseFrame) |
| 1695 | { |
| 1696 | QByteArray payload = QByteArray::fromHex(hexEncoded: utf8Sequence); |
| 1697 | |
| 1698 | if (isCloseFrame) |
| 1699 | { |
| 1700 | quint16 closeCode = qToBigEndian<quint16>(source: QWebSocketProtocol::CloseCodeNormal); |
| 1701 | const char *wireRepresentation |
| 1702 | = static_cast<const char *>(static_cast<const void *>(&closeCode)); |
| 1703 | QTest::newRow(QStringLiteral("Close frame with invalid UTF8-sequence: %1" ) |
| 1704 | .arg(a: dataTag).toLatin1().constData()) |
| 1705 | << quint8(FIN | QWebSocketProtocol::OpCodeClose) |
| 1706 | << quint8(payload.length() + 2) |
| 1707 | << QByteArray(wireRepresentation, 2).append(a: payload) |
| 1708 | << false |
| 1709 | << QWebSocketProtocol::CloseCodeWrongDatatype; |
| 1710 | } |
| 1711 | else |
| 1712 | { |
| 1713 | QTest::newRow(QStringLiteral("Text frame with invalid UTF8-sequence: %1" ) |
| 1714 | .arg(a: dataTag).toLatin1().constData()) |
| 1715 | << quint8(FIN | QWebSocketProtocol::OpCodeText) |
| 1716 | << quint8(payload.length()) |
| 1717 | << payload |
| 1718 | << false |
| 1719 | << QWebSocketProtocol::CloseCodeWrongDatatype; |
| 1720 | |
| 1721 | QTest::newRow(QStringLiteral("Continuation text frame with invalid UTF8-sequence: %1" ) |
| 1722 | .arg(a: dataTag).toLatin1().constData()) |
| 1723 | << quint8(FIN | QWebSocketProtocol::OpCodeContinue) |
| 1724 | << quint8(payload.length()) |
| 1725 | << payload |
| 1726 | << true |
| 1727 | << QWebSocketProtocol::CloseCodeWrongDatatype; |
| 1728 | } |
| 1729 | } |
| 1730 | |
| 1731 | void tst_DataProcessor::invalidField(const char *dataTag, quint8 invalidFieldValue) |
| 1732 | { |
| 1733 | QTest::newRow(dataTag) << quint8(FIN | invalidFieldValue) |
| 1734 | << quint8(0x00) |
| 1735 | << QByteArray() |
| 1736 | << false |
| 1737 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1738 | QTest::newRow(dataTag: QString::fromLatin1(str: dataTag).append(QStringLiteral(" with continuation frame" )) |
| 1739 | .toLatin1().constData()) |
| 1740 | << quint8(FIN | invalidFieldValue) |
| 1741 | << quint8(0x00) |
| 1742 | << QByteArray() |
| 1743 | << true |
| 1744 | << QWebSocketProtocol::CloseCodeProtocolError; |
| 1745 | } |
| 1746 | |
| 1747 | void tst_DataProcessor::incompleteFrame(quint8 controlCode, quint64 indicatedSize, |
| 1748 | quint64 actualPayloadSize) |
| 1749 | { |
| 1750 | QVERIFY(!QWebSocketProtocol::isOpCodeReserved(QWebSocketProtocol::OpCode(controlCode))); |
| 1751 | QVERIFY(indicatedSize > actualPayloadSize); |
| 1752 | |
| 1753 | QString frameType = opCodeToString(opCode: controlCode); |
| 1754 | QByteArray firstFrame; |
| 1755 | |
| 1756 | if (indicatedSize < 126) |
| 1757 | { |
| 1758 | //doing extensive QStringLiteral concatenations here, because |
| 1759 | //MSVC 2010 complains when using concatenation literal strings about |
| 1760 | //concatenation of wide and narrow strings (error C2308) |
| 1761 | QTest::newRow(dataTag: frameType |
| 1762 | .append(QStringLiteral(" frame with payload size %1, but only %2 bytes" ) + |
| 1763 | QStringLiteral(" of data" ) |
| 1764 | .arg(a: indicatedSize).arg(a: actualPayloadSize)).toLatin1().constData()) |
| 1765 | << quint8(FIN | controlCode) |
| 1766 | << quint8(indicatedSize) |
| 1767 | << firstFrame.append(a: QByteArray(actualPayloadSize, 'a')) |
| 1768 | << (controlCode == QWebSocketProtocol::OpCodeContinue) |
| 1769 | << QWebSocketProtocol::CloseCodeGoingAway; |
| 1770 | } |
| 1771 | else if (indicatedSize <= 0xFFFFu) |
| 1772 | { |
| 1773 | quint16 swapped16 = qToBigEndian<quint16>(source: static_cast<quint16>(indicatedSize)); |
| 1774 | const char *wireRepresentation |
| 1775 | = static_cast<const char *>(static_cast<const void *>(&swapped16)); |
| 1776 | QTest::newRow( |
| 1777 | dataTag: frameType.append(QStringLiteral(" frame with payload size %1, but only " ) + |
| 1778 | QStringLiteral("%2 bytes of data" ) |
| 1779 | .arg(a: indicatedSize) |
| 1780 | .arg(a: actualPayloadSize)).toLatin1().constData()) |
| 1781 | << quint8(FIN | controlCode) |
| 1782 | << quint8(126) |
| 1783 | << firstFrame.append(a: QByteArray(wireRepresentation, 2) |
| 1784 | .append(a: QByteArray(actualPayloadSize, 'a'))) |
| 1785 | << (controlCode == QWebSocketProtocol::OpCodeContinue) |
| 1786 | << QWebSocketProtocol::CloseCodeGoingAway; |
| 1787 | } |
| 1788 | else |
| 1789 | { |
| 1790 | quint64 swapped64 = qToBigEndian<quint64>(source: indicatedSize); |
| 1791 | const char *wireRepresentation |
| 1792 | = static_cast<const char *>(static_cast<const void *>(&swapped64)); |
| 1793 | QTest::newRow(dataTag: frameType |
| 1794 | .append(QStringLiteral(" frame with payload size %1, but only %2 bytes " ) + |
| 1795 | QStringLiteral("of data" ) |
| 1796 | .arg(a: indicatedSize).arg(a: actualPayloadSize)).toLatin1().constData()) |
| 1797 | << quint8(FIN | controlCode) |
| 1798 | << quint8(127) |
| 1799 | << firstFrame.append(a: QByteArray(wireRepresentation, 8) |
| 1800 | .append(a: QByteArray(actualPayloadSize, 'a'))) |
| 1801 | << (controlCode == QWebSocketProtocol::OpCodeContinue) |
| 1802 | << QWebSocketProtocol::CloseCodeGoingAway; |
| 1803 | } |
| 1804 | } |
| 1805 | |
| 1806 | void tst_DataProcessor::nonCharacterSequence(const char *sequence) |
| 1807 | { |
| 1808 | QByteArray utf8Sequence = QByteArray::fromHex(hexEncoded: sequence); |
| 1809 | |
| 1810 | //doing extensive QStringLiteral concatenations here, because |
| 1811 | //MSVC 2010 complains when using concatenation literal strings about |
| 1812 | //concatenation of wide and narrow strings (error C2308) |
| 1813 | QTest::newRow(dataTag: (QStringLiteral("Text frame with payload containing the non-control character " ) + |
| 1814 | QStringLiteral("sequence 0x%1" )) |
| 1815 | .arg(a: QString::fromLocal8Bit(str: sequence)).toLatin1().constData()) |
| 1816 | << quint8(FIN | QWebSocketProtocol::OpCodeText) |
| 1817 | << quint8(utf8Sequence.size()) |
| 1818 | << utf8Sequence |
| 1819 | << false; |
| 1820 | |
| 1821 | QTest::newRow(dataTag: (QStringLiteral("Continuation frame with payload containing the non-control " ) + |
| 1822 | QStringLiteral("character sequence 0x%1" )) |
| 1823 | .arg(a: QString::fromLocal8Bit(str: sequence)).toLatin1().constData()) |
| 1824 | << quint8(FIN | QWebSocketProtocol::OpCodeContinue) |
| 1825 | << quint8(utf8Sequence.size()) |
| 1826 | << utf8Sequence |
| 1827 | << true; |
| 1828 | } |
| 1829 | |
| 1830 | void tst_DataProcessor::insertIncompleteSizeFieldTest(quint8 payloadCode, quint8 numBytesFollowing) |
| 1831 | { |
| 1832 | //doing extensive QStringLiteral concatenations here, because |
| 1833 | //MSVC 2010 complains when using concatenation literal strings about |
| 1834 | //concatenation of wide and narrow strings (error C2308) |
| 1835 | QTest::newRow(QStringLiteral("Text frame with payload size %1, with %2 bytes following." ) |
| 1836 | .arg(a: payloadCode).arg(a: numBytesFollowing).toLatin1().constData()) |
| 1837 | << quint8(FIN | QWebSocketProtocol::OpCodeText) |
| 1838 | << quint8(payloadCode) |
| 1839 | << QByteArray(numBytesFollowing, quint8(1)) |
| 1840 | << false |
| 1841 | << QWebSocketProtocol::CloseCodeGoingAway; |
| 1842 | QTest::newRow(QStringLiteral("Binary frame with payload size %1, with %2 bytes following." ) |
| 1843 | .arg(a: payloadCode).arg(a: numBytesFollowing).toLatin1().constData()) |
| 1844 | << quint8(FIN | QWebSocketProtocol::OpCodeBinary) |
| 1845 | << quint8(payloadCode) |
| 1846 | << QByteArray(numBytesFollowing, quint8(1)) |
| 1847 | << false |
| 1848 | << QWebSocketProtocol::CloseCodeGoingAway; |
| 1849 | QTest::newRow(dataTag: (QStringLiteral("Continuation frame with payload size %1, with %2 bytes " ) + |
| 1850 | QStringLiteral("following." )) |
| 1851 | .arg(a: payloadCode).arg(a: numBytesFollowing).toLatin1().constData()) |
| 1852 | << quint8(FIN | QWebSocketProtocol::OpCodeContinue) |
| 1853 | << quint8(payloadCode) |
| 1854 | << QByteArray(numBytesFollowing, quint8(1)) |
| 1855 | << true |
| 1856 | << QWebSocketProtocol::CloseCodeGoingAway; |
| 1857 | } |
| 1858 | |
| 1859 | void tst_DataProcessor::clearDataBuffers() |
| 1860 | { |
| 1861 | const QByteArray binaryData("Hello!" ); |
| 1862 | QByteArray data; |
| 1863 | data.append(c: char(FIN | QWebSocketProtocol::OpCodeBinary)); |
| 1864 | data.append(c: char(binaryData.length())); |
| 1865 | data.append(a: binaryData); |
| 1866 | |
| 1867 | QWebSocketDataProcessor dataProcessor; |
| 1868 | connect(sender: &dataProcessor, signal: &QWebSocketDataProcessor::binaryMessageReceived, |
| 1869 | slot: [&binaryData](const QByteArray &message) |
| 1870 | { |
| 1871 | QCOMPARE(message, binaryData); |
| 1872 | QEventLoop loop; |
| 1873 | QTimer::singleShot(msec: 2000, receiver: &loop, SLOT(quit())); |
| 1874 | loop.exec(); |
| 1875 | }); |
| 1876 | |
| 1877 | QBuffer buffer; |
| 1878 | buffer.setData(data); |
| 1879 | auto processData = [&dataProcessor, &buffer]() |
| 1880 | { |
| 1881 | buffer.open(openMode: QIODevice::ReadOnly); |
| 1882 | dataProcessor.process(pIoDevice: &buffer); |
| 1883 | buffer.close(); |
| 1884 | }; |
| 1885 | |
| 1886 | QTimer timer; |
| 1887 | timer.setSingleShot(true); |
| 1888 | connect(sender: &timer, signal: &QTimer::timeout, slot: processData); |
| 1889 | |
| 1890 | timer.start(msec: 1000); |
| 1891 | processData(); |
| 1892 | QTest::qWait(ms: 2000); |
| 1893 | } |
| 1894 | |
| 1895 | QTEST_MAIN(tst_DataProcessor) |
| 1896 | |
| 1897 | #include "tst_dataprocessor.moc" |
| 1898 | |