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 | |