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 <QDebug> |
31 | #include <QByteArray> |
32 | #include <QtEndian> |
33 | |
34 | #include "private/qwebsocketframe_p.h" |
35 | #include "private/qwebsocketprotocol_p.h" |
36 | #include "qwebsocketprotocol.h" |
37 | |
38 | QT_USE_NAMESPACE |
39 | |
40 | Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode) |
41 | Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode) |
42 | |
43 | /*! |
44 | * \brief class FrameHelper is used to encode a single frame. |
45 | * |
46 | * \internal |
47 | */ |
48 | class FrameHelper |
49 | { |
50 | public: |
51 | FrameHelper(); |
52 | |
53 | QByteArray wireRepresentation(); |
54 | |
55 | void setRsv1(int value) { m_rsv1 = value; } |
56 | void setRsv2(int value) { m_rsv2 = value; } |
57 | void setRsv3(int value) { m_rsv3 = value; } |
58 | void setMask(quint32 mask) { m_mask = mask; } |
59 | void setOpCode(QWebSocketProtocol::OpCode opCode) { m_opCode = opCode; } |
60 | void setPayload(const QByteArray &payload) { m_payload = payload; } |
61 | void setFinalFrame(bool isFinal) { m_isFinalFrame = isFinal; } |
62 | |
63 | private: |
64 | int m_rsv1; |
65 | int m_rsv2; |
66 | int m_rsv3; |
67 | quint32 m_mask; |
68 | QWebSocketProtocol::OpCode m_opCode; |
69 | QByteArray m_payload; |
70 | bool m_isFinalFrame; |
71 | }; |
72 | |
73 | FrameHelper::FrameHelper() : |
74 | m_rsv1(0), m_rsv2(0), m_rsv3(0), |
75 | m_mask(0), m_opCode(QWebSocketProtocol::OpCodeReserved3), |
76 | m_payload(), m_isFinalFrame(false) |
77 | {} |
78 | |
79 | QByteArray FrameHelper::wireRepresentation() |
80 | { |
81 | quint8 byte = 0x00; |
82 | QByteArray wireRep; |
83 | quint64 payloadLength = m_payload.length(); |
84 | |
85 | //FIN, opcode |
86 | byte = static_cast<quint8>((m_opCode & 0x0F) | (m_isFinalFrame ? 0x80 : 0x00)); //FIN, opcode |
87 | //RSV1-3 |
88 | byte |= static_cast<quint8>(((m_rsv1 & 0x01) << 6) | ((m_rsv2 & 0x01) << 5) | |
89 | ((m_rsv3 & 0x01) << 4)); |
90 | wireRep.append(c: static_cast<char>(byte)); |
91 | |
92 | byte = 0x00; |
93 | if (m_mask != 0) |
94 | { |
95 | byte |= 0x80; |
96 | } |
97 | if (payloadLength <= 125) |
98 | { |
99 | byte |= static_cast<quint8>(payloadLength); |
100 | wireRep.append(c: static_cast<char>(byte)); |
101 | } |
102 | else if (payloadLength <= 0xFFFFU) |
103 | { |
104 | byte |= 126; |
105 | wireRep.append(c: static_cast<char>(byte)); |
106 | quint16 swapped = qToBigEndian<quint16>(source: static_cast<quint16>(payloadLength)); |
107 | wireRep.append(s: static_cast<const char *>(static_cast<const void *>(&swapped)), len: 2); |
108 | } |
109 | else |
110 | { |
111 | byte |= 127; |
112 | wireRep.append(c: static_cast<char>(byte)); |
113 | quint64 swapped = qToBigEndian<quint64>(source: payloadLength); |
114 | wireRep.append(s: static_cast<const char *>(static_cast<const void *>(&swapped)), len: 8); |
115 | } |
116 | //Write mask |
117 | if (m_mask != 0) |
118 | { |
119 | wireRep.append(s: static_cast<const char *>(static_cast<const void *>(&m_mask)), |
120 | len: sizeof(quint32)); |
121 | } |
122 | QByteArray tmpData = m_payload; |
123 | if (m_mask) |
124 | { |
125 | tmpData.detach(); |
126 | QWebSocketProtocol::mask(payload: &tmpData, maskingKey: m_mask); |
127 | } |
128 | wireRep.append(a: tmpData); |
129 | return wireRep; |
130 | } |
131 | |
132 | class tst_WebSocketFrame : public QObject |
133 | { |
134 | Q_OBJECT |
135 | |
136 | public: |
137 | tst_WebSocketFrame(); |
138 | |
139 | private Q_SLOTS: |
140 | void initTestCase(); |
141 | void cleanupTestCase(); |
142 | void init(); |
143 | void cleanup(); |
144 | |
145 | void tst_initialization(); |
146 | void tst_copyConstructorAndAssignment(); |
147 | |
148 | void tst_goodFrames_data(); |
149 | void tst_goodFrames(); |
150 | |
151 | void tst_invalidFrames_data(); |
152 | void tst_invalidFrames(); |
153 | |
154 | void tst_malformedFrames_data(); |
155 | void tst_malformedFrames(); |
156 | }; |
157 | |
158 | tst_WebSocketFrame::tst_WebSocketFrame() |
159 | {} |
160 | |
161 | void tst_WebSocketFrame::initTestCase() |
162 | { |
163 | } |
164 | |
165 | void tst_WebSocketFrame::cleanupTestCase() |
166 | {} |
167 | |
168 | void tst_WebSocketFrame::init() |
169 | { |
170 | qRegisterMetaType<QWebSocketProtocol::OpCode>(typeName: "QWebSocketProtocol::OpCode" ); |
171 | qRegisterMetaType<QWebSocketProtocol::CloseCode>(typeName: "QWebSocketProtocol::CloseCode" ); |
172 | } |
173 | |
174 | void tst_WebSocketFrame::cleanup() |
175 | { |
176 | } |
177 | |
178 | void tst_WebSocketFrame::tst_initialization() |
179 | { |
180 | QWebSocketFrame frame; |
181 | QVERIFY(!frame.isValid()); |
182 | QCOMPARE(frame.payload().length(), 0); |
183 | } |
184 | |
185 | void tst_WebSocketFrame::tst_copyConstructorAndAssignment() |
186 | { |
187 | FrameHelper frameHelper; |
188 | frameHelper.setRsv1(0); |
189 | frameHelper.setRsv2(0); |
190 | frameHelper.setRsv3(0); |
191 | frameHelper.setFinalFrame(true); |
192 | frameHelper.setMask(1234u); |
193 | frameHelper.setOpCode(QWebSocketProtocol::OpCodeBinary); |
194 | frameHelper.setPayload(QByteArrayLiteral("12345" )); |
195 | |
196 | QByteArray payload = frameHelper.wireRepresentation(); |
197 | QBuffer buffer(&payload); |
198 | buffer.open(openMode: QIODevice::ReadOnly); |
199 | |
200 | QWebSocketFrame frame; |
201 | frame.readFrame(pIoDevice: &buffer); |
202 | buffer.close(); |
203 | |
204 | auto compareFrames = [](const QWebSocketFrame &other, const QWebSocketFrame &frame) |
205 | { |
206 | QCOMPARE(other.closeCode(), frame.closeCode()); |
207 | QCOMPARE(other.closeReason(), frame.closeReason()); |
208 | QCOMPARE(other.hasMask(), frame.hasMask()); |
209 | QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame()); |
210 | QCOMPARE(other.isControlFrame(), frame.isControlFrame()); |
211 | QCOMPARE(other.isDataFrame(), frame.isDataFrame()); |
212 | QCOMPARE(other.isFinalFrame(), frame.isFinalFrame()); |
213 | QCOMPARE(other.isValid(), frame.isValid()); |
214 | QCOMPARE(other.mask(), frame.mask()); |
215 | QCOMPARE(other.opCode(), frame.opCode()); |
216 | QCOMPARE(other.payload(), frame.payload()); |
217 | QCOMPARE(other.rsv1(), frame.rsv1()); |
218 | QCOMPARE(other.rsv2(), frame.rsv2()); |
219 | QCOMPARE(other.rsv3(), frame.rsv3()); |
220 | }; |
221 | |
222 | { |
223 | QWebSocketFrame other(frame); |
224 | compareFrames(other, frame); |
225 | } |
226 | { |
227 | QWebSocketFrame other; |
228 | other = frame; |
229 | compareFrames(other, frame); |
230 | QWebSocketFrame other2 = std::move(other); |
231 | compareFrames(other2, frame); |
232 | QWebSocketFrame other3(std::move(other2)); |
233 | compareFrames(other3, frame); |
234 | } |
235 | } |
236 | |
237 | void tst_WebSocketFrame::tst_goodFrames_data() |
238 | { |
239 | QTest::addColumn<bool>(name: "rsv1" ); |
240 | QTest::addColumn<bool>(name: "rsv2" ); |
241 | QTest::addColumn<bool>(name: "rsv3" ); |
242 | QTest::addColumn<quint32>(name: "mask" ); |
243 | QTest::addColumn<QWebSocketProtocol::OpCode>(name: "opCode" ); |
244 | QTest::addColumn<bool>(name: "isFinal" ); |
245 | QTest::addColumn<QByteArray>(name: "payload" ); |
246 | QTest::addColumn<bool>(name: "isControlFrame" ); |
247 | QTest::addColumn<bool>(name: "isDataFrame" ); |
248 | QTest::addColumn<bool>(name: "isContinuationFrame" ); |
249 | |
250 | QTest::newRow(dataTag: "Non masked final text frame with small payload" ) |
251 | << false << false << false |
252 | << 0U << QWebSocketProtocol::OpCodeText |
253 | << true << QStringLiteral("Hello world!" ).toUtf8() |
254 | << false << true << false; |
255 | QTest::newRow(dataTag: "Non masked final binary frame with small payload" ) |
256 | << false << false << false |
257 | << 0U << QWebSocketProtocol::OpCodeBinary |
258 | << true << QByteArrayLiteral("\x00\x01\x02\x03\x04" ) |
259 | << false << true << false; |
260 | QTest::newRow(dataTag: "Non masked final text frame with no payload" ) |
261 | << false << false << false |
262 | << 0U << QWebSocketProtocol::OpCodeText |
263 | << true << QByteArray() |
264 | << false << true << false; |
265 | QTest::newRow(dataTag: "Non masked final binary frame with no payload" ) |
266 | << false << false << false |
267 | << 0U << QWebSocketProtocol::OpCodeBinary |
268 | << true << QByteArray() |
269 | << false << true << false; |
270 | |
271 | QTest::newRow(dataTag: "Non masked final close frame with small payload" ) |
272 | << false << false << false |
273 | << 0U << QWebSocketProtocol::OpCodeClose |
274 | << true << QStringLiteral("Hello world!" ).toUtf8() |
275 | << true << false << false; |
276 | QTest::newRow(dataTag: "Non masked final close frame with no payload" ) |
277 | << false << false << false |
278 | << 0U << QWebSocketProtocol::OpCodeClose |
279 | << true << QByteArray() |
280 | << true << false << false; |
281 | QTest::newRow(dataTag: "Non masked final ping frame with small payload" ) |
282 | << false << false << false |
283 | << 0U << QWebSocketProtocol::OpCodePing |
284 | << true << QStringLiteral("Hello world!" ).toUtf8() |
285 | << true << false << false; |
286 | QTest::newRow(dataTag: "Non masked final pong frame with no payload" ) |
287 | << false << false << false |
288 | << 0U << QWebSocketProtocol::OpCodePong |
289 | << true << QByteArray() |
290 | << true << false << false; |
291 | |
292 | QTest::newRow(dataTag: "Non masked final continuation frame with small payload" ) |
293 | << false << false << false |
294 | << 0U << QWebSocketProtocol::OpCodeContinue |
295 | << true << QStringLiteral("Hello world!" ).toUtf8() |
296 | << false << true << true; |
297 | QTest::newRow(dataTag: "Non masked non-final continuation frame with small payload" ) |
298 | << false << false << false |
299 | << 0U << QWebSocketProtocol::OpCodeContinue |
300 | << false << QStringLiteral("Hello world!" ).toUtf8() |
301 | << false << true << true; |
302 | } |
303 | |
304 | void tst_WebSocketFrame::tst_goodFrames() |
305 | { |
306 | QFETCH(bool, rsv1); |
307 | QFETCH(bool, rsv2); |
308 | QFETCH(bool, rsv3); |
309 | QFETCH(quint32, mask); |
310 | QFETCH(QWebSocketProtocol::OpCode, opCode); |
311 | QFETCH(bool, isFinal); |
312 | QFETCH(QByteArray, payload); |
313 | QFETCH(bool, isControlFrame); |
314 | QFETCH(bool, isDataFrame); |
315 | QFETCH(bool, isContinuationFrame); |
316 | |
317 | FrameHelper helper; |
318 | helper.setRsv1(rsv1); |
319 | helper.setRsv2(rsv2); |
320 | helper.setRsv3(rsv3); |
321 | helper.setMask(mask); |
322 | helper.setOpCode(opCode); |
323 | helper.setFinalFrame(isFinal); |
324 | helper.setPayload(payload); |
325 | |
326 | QByteArray wireRepresentation = helper.wireRepresentation(); |
327 | QBuffer buffer; |
328 | buffer.setData(wireRepresentation); |
329 | buffer.open(openMode: QIODevice::ReadOnly); |
330 | QWebSocketFrame frame; |
331 | frame.readFrame(pIoDevice: &buffer); |
332 | buffer.close(); |
333 | QVERIFY(frame.isValid()); |
334 | QCOMPARE(frame.rsv1(), rsv1); |
335 | QCOMPARE(frame.rsv2(), rsv2); |
336 | QCOMPARE(frame.rsv3(), rsv3); |
337 | QCOMPARE(frame.hasMask(), (mask != 0)); |
338 | QCOMPARE(frame.opCode(), opCode); |
339 | QCOMPARE(frame.isFinalFrame(), isFinal); |
340 | QCOMPARE(frame.isControlFrame(), isControlFrame); |
341 | QCOMPARE(frame.isDataFrame(), isDataFrame); |
342 | QCOMPARE(frame.isContinuationFrame(), isContinuationFrame); |
343 | QCOMPARE(frame.payload().length(), payload.length()); |
344 | QCOMPARE(frame.payload(), payload); |
345 | } |
346 | |
347 | void tst_WebSocketFrame::tst_invalidFrames_data() |
348 | { |
349 | QTest::addColumn<int>(name: "rsv1" ); |
350 | QTest::addColumn<int>(name: "rsv2" ); |
351 | QTest::addColumn<int>(name: "rsv3" ); |
352 | QTest::addColumn<quint32>(name: "mask" ); |
353 | QTest::addColumn<QWebSocketProtocol::OpCode>(name: "opCode" ); |
354 | QTest::addColumn<bool>(name: "isFinal" ); |
355 | QTest::addColumn<QByteArray>(name: "payload" ); |
356 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "expectedError" ); |
357 | |
358 | QTest::newRow(dataTag: "RSV1 != 0" ) |
359 | << 1 << 0 << 0 |
360 | << 0U << QWebSocketProtocol::OpCodeText |
361 | << true << QStringLiteral("Hello world!" ).toUtf8() |
362 | << QWebSocketProtocol::CloseCodeProtocolError; |
363 | QTest::newRow(dataTag: "RSV2 != 0" ) |
364 | << 0 << 1 << 0 |
365 | << 0U << QWebSocketProtocol::OpCodeText |
366 | << true << QStringLiteral("Hello world!" ).toUtf8() |
367 | << QWebSocketProtocol::CloseCodeProtocolError; |
368 | QTest::newRow(dataTag: "RSV3 != 0" ) |
369 | << 0 << 0 << 1 |
370 | << 0U << QWebSocketProtocol::OpCodeText |
371 | << true << QStringLiteral("Hello world!" ).toUtf8() |
372 | << QWebSocketProtocol::CloseCodeProtocolError; |
373 | QTest::newRow(dataTag: "RSV1 != 0 and RSV2 != 0" ) |
374 | << 1 << 1 << 0 |
375 | << 0U << QWebSocketProtocol::OpCodeText |
376 | << true << QStringLiteral("Hello world!" ).toUtf8() |
377 | << QWebSocketProtocol::CloseCodeProtocolError; |
378 | QTest::newRow(dataTag: "RSV1 != 0 and RSV3 != 0" ) |
379 | << 1 << 0 << 1 |
380 | << 0U << QWebSocketProtocol::OpCodeText |
381 | << true << QStringLiteral("Hello world!" ).toUtf8() |
382 | << QWebSocketProtocol::CloseCodeProtocolError; |
383 | QTest::newRow(dataTag: "RSV2 != 0 and RSV3 != 0" ) |
384 | << 0 << 1 << 1 |
385 | << 0U << QWebSocketProtocol::OpCodeText |
386 | << true << QStringLiteral("Hello world!" ).toUtf8() |
387 | << QWebSocketProtocol::CloseCodeProtocolError; |
388 | |
389 | QTest::newRow(dataTag: "Reserved OpCode 3" ) |
390 | << 0 << 0 << 0 |
391 | << 0U << QWebSocketProtocol::OpCodeReserved3 |
392 | << true << QStringLiteral("Hello world!" ).toUtf8() |
393 | << QWebSocketProtocol::CloseCodeProtocolError; |
394 | QTest::newRow(dataTag: "Reserved OpCode 4" ) |
395 | << 0 << 0 << 0 |
396 | << 0U << QWebSocketProtocol::OpCodeReserved4 |
397 | << true << QStringLiteral("Hello world!" ).toUtf8() |
398 | << QWebSocketProtocol::CloseCodeProtocolError; |
399 | QTest::newRow(dataTag: "Reserved OpCode 5" ) |
400 | << 0 << 0 << 0 |
401 | << 0U << QWebSocketProtocol::OpCodeReserved5 |
402 | << true << QStringLiteral("Hello world!" ).toUtf8() |
403 | << QWebSocketProtocol::CloseCodeProtocolError; |
404 | QTest::newRow(dataTag: "Reserved OpCode 6" ) |
405 | << 0 << 0 << 0 |
406 | << 0U << QWebSocketProtocol::OpCodeReserved6 |
407 | << true << QStringLiteral("Hello world!" ).toUtf8() |
408 | << QWebSocketProtocol::CloseCodeProtocolError; |
409 | QTest::newRow(dataTag: "Reserved OpCode 7" ) |
410 | << 0 << 0 << 0 |
411 | << 0U << QWebSocketProtocol::OpCodeReserved7 |
412 | << true << QStringLiteral("Hello world!" ).toUtf8() |
413 | << QWebSocketProtocol::CloseCodeProtocolError; |
414 | QTest::newRow(dataTag: "Reserved OpCode B" ) |
415 | << 0 << 0 << 0 |
416 | << 0U << QWebSocketProtocol::OpCodeReservedB |
417 | << true << QStringLiteral("Hello world!" ).toUtf8() |
418 | << QWebSocketProtocol::CloseCodeProtocolError; |
419 | QTest::newRow(dataTag: "Reserved OpCode C" ) |
420 | << 0 << 0 << 0 |
421 | << 0U << QWebSocketProtocol::OpCodeReservedC |
422 | << true << QStringLiteral("Hello world!" ).toUtf8() |
423 | << QWebSocketProtocol::CloseCodeProtocolError; |
424 | QTest::newRow(dataTag: "Reserved OpCode D" ) |
425 | << 0 << 0 << 0 |
426 | << 0U << QWebSocketProtocol::OpCodeReservedD |
427 | << true << QStringLiteral("Hello world!" ).toUtf8() |
428 | << QWebSocketProtocol::CloseCodeProtocolError; |
429 | QTest::newRow(dataTag: "Reserved OpCode E" ) |
430 | << 0 << 0 << 0 |
431 | << 0U << QWebSocketProtocol::OpCodeReservedE |
432 | << true << QStringLiteral("Hello world!" ).toUtf8() |
433 | << QWebSocketProtocol::CloseCodeProtocolError; |
434 | QTest::newRow(dataTag: "Reserved OpCode F" ) |
435 | << 0 << 0 << 0 |
436 | << 0U << QWebSocketProtocol::OpCodeReservedF |
437 | << true << QStringLiteral("Hello world!" ).toUtf8() |
438 | << QWebSocketProtocol::CloseCodeProtocolError; |
439 | |
440 | QTest::newRow(dataTag: "Close Frame with payload > 125 bytes" ) |
441 | << 0 << 0 << 0 |
442 | << 0U << QWebSocketProtocol::OpCodeClose |
443 | << true << QString(126, 'a').toUtf8() |
444 | << QWebSocketProtocol::CloseCodeProtocolError; |
445 | QTest::newRow(dataTag: "Non-final Close Frame" ) |
446 | << 0 << 0 << 0 |
447 | << 0U << QWebSocketProtocol::OpCodeClose |
448 | << false << QString(126, 'a').toUtf8() |
449 | << QWebSocketProtocol::CloseCodeProtocolError; |
450 | QTest::newRow(dataTag: "Ping Frame with payload > 125 bytes" ) |
451 | << 0 << 0 << 0 |
452 | << 0U << QWebSocketProtocol::OpCodePing |
453 | << true << QString(126, 'a').toUtf8() |
454 | << QWebSocketProtocol::CloseCodeProtocolError; |
455 | QTest::newRow(dataTag: "Non-final Ping Frame" ) |
456 | << 0 << 0 << 0 |
457 | << 0U << QWebSocketProtocol::OpCodePing |
458 | << false << QString(126, 'a').toUtf8() |
459 | << QWebSocketProtocol::CloseCodeProtocolError; |
460 | QTest::newRow(dataTag: "Pong Frame with payload > 125 bytes" ) |
461 | << 0 << 0 << 0 |
462 | << 0U << QWebSocketProtocol::OpCodePong |
463 | << true << QString(126, 'a').toUtf8() |
464 | << QWebSocketProtocol::CloseCodeProtocolError; |
465 | QTest::newRow(dataTag: "Non-final Pong Frame" ) |
466 | << 0 << 0 << 0 |
467 | << 0U << QWebSocketProtocol::OpCodePong |
468 | << false << QString(126, 'a').toUtf8() |
469 | << QWebSocketProtocol::CloseCodeProtocolError; |
470 | } |
471 | |
472 | void tst_WebSocketFrame::tst_invalidFrames() |
473 | { |
474 | QFETCH(int, rsv1); |
475 | QFETCH(int, rsv2); |
476 | QFETCH(int, rsv3); |
477 | QFETCH(quint32, mask); |
478 | QFETCH(QWebSocketProtocol::OpCode, opCode); |
479 | QFETCH(bool, isFinal); |
480 | QFETCH(QByteArray, payload); |
481 | QFETCH(QWebSocketProtocol::CloseCode, expectedError); |
482 | |
483 | FrameHelper helper; |
484 | helper.setRsv1(rsv1); |
485 | helper.setRsv2(rsv2); |
486 | helper.setRsv3(rsv3); |
487 | helper.setMask(mask); |
488 | helper.setOpCode(opCode); |
489 | helper.setFinalFrame(isFinal); |
490 | helper.setPayload(payload); |
491 | |
492 | QByteArray wireRepresentation = helper.wireRepresentation(); |
493 | QBuffer buffer; |
494 | buffer.setData(wireRepresentation); |
495 | buffer.open(openMode: QIODevice::ReadOnly); |
496 | QWebSocketFrame frame; |
497 | frame.readFrame(pIoDevice: &buffer); |
498 | buffer.close(); |
499 | |
500 | QVERIFY(!frame.isValid()); |
501 | QCOMPARE(frame.closeCode(), expectedError); |
502 | } |
503 | |
504 | |
505 | /* |
506 | * Incomplete or overly large frames |
507 | * Payload must be crafted manually |
508 | * |
509 | QTest::newRow("Frame Too Big") |
510 | << 0 << 0 << 0 |
511 | << 0U << QWebSocketProtocol::OpCodeText |
512 | << true << QString(MAX_FRAME_SIZE_IN_BYTES + 1, 'a').toUtf8() |
513 | << QWebSocketProtocol::CloseCodeTooMuchData; |
514 | |
515 | */ |
516 | void tst_WebSocketFrame::tst_malformedFrames_data() |
517 | { |
518 | QTest::addColumn<QByteArray>(name: "payload" ); |
519 | QTest::addColumn<QWebSocketProtocol::CloseCode>(name: "expectedError" ); |
520 | |
521 | //too little data |
522 | QTest::newRow(dataTag: "No data" ) << QByteArray() << QWebSocketProtocol::CloseCodeGoingAway; |
523 | FrameHelper helper; |
524 | helper.setRsv1(0); |
525 | helper.setRsv2(0); |
526 | helper.setRsv3(0); |
527 | helper.setMask(0U); |
528 | helper.setOpCode(QWebSocketProtocol::OpCodeText); |
529 | helper.setFinalFrame(true); |
530 | helper.setPayload(QString(10, 'a').toUtf8()); |
531 | QByteArray wireRep = helper.wireRepresentation(); |
532 | |
533 | //too little data |
534 | //header + payload should be 12 bytes for non-masked payloads < 126 bytes |
535 | for (int i = 1; i < 12; ++i) |
536 | { |
537 | QTest::newRow(QStringLiteral("Header too small - %1 byte(s)" ).arg(a: i).toLatin1().constData()) |
538 | << wireRep.left(len: i) |
539 | << QWebSocketProtocol::CloseCodeGoingAway; |
540 | } |
541 | //too much data |
542 | { |
543 | const char bigpayloadIndicator = char(127); |
544 | const quint64 payloadSize = QWebSocketFrame::maxFrameSize() + 1; |
545 | uchar swapped[8] = {0}; |
546 | qToBigEndian<quint64>(src: payloadSize, dest: swapped); |
547 | QTest::newRow(dataTag: "Frame too big" ) |
548 | << wireRep.left(len: 1).append(c: bigpayloadIndicator) |
549 | .append(s: reinterpret_cast<char *>(swapped), len: 8) |
550 | << QWebSocketProtocol::CloseCodeTooMuchData; |
551 | } |
552 | //invalid size field |
553 | { |
554 | const char bigpayloadIndicator = char(127); |
555 | quint64 payloadSize = quint64(1) << 63; |
556 | uchar swapped[8] = {0}; |
557 | qToBigEndian<quint64>(src: payloadSize, dest: swapped); |
558 | QTest::newRow(dataTag: "Highest bit of payload length is set" ) |
559 | << wireRep.left(len: 1).append(c: bigpayloadIndicator) |
560 | .append(s: reinterpret_cast<char *>(swapped), len: 8) |
561 | << QWebSocketProtocol::CloseCodeProtocolError; |
562 | |
563 | payloadSize = 256; |
564 | qToBigEndian<quint64>(src: payloadSize, dest: swapped); |
565 | QTest::newRow(dataTag: "Overlong 64-bit size field; should be 16-bit" ) |
566 | << wireRep.left(len: 1).append(c: bigpayloadIndicator) |
567 | .append(s: reinterpret_cast<char *>(swapped), len: 8) |
568 | << QWebSocketProtocol::CloseCodeProtocolError; |
569 | } |
570 | //overlong size field |
571 | { |
572 | const char largepayloadIndicator = char(126); |
573 | const quint16 payloadSize = 120; |
574 | uchar swapped[2] = {0}; |
575 | qToBigEndian<quint16>(src: payloadSize, dest: swapped); |
576 | QTest::newRow(dataTag: "Overlong 16-bit size field" ) |
577 | << wireRep.left(len: 1).append(c: largepayloadIndicator) |
578 | .append(s: reinterpret_cast<char *>(swapped), len: 2) |
579 | << QWebSocketProtocol::CloseCodeProtocolError; |
580 | } |
581 | { |
582 | const char bigpayloadIndicator = char(127); |
583 | quint64 payloadSize = 120; |
584 | uchar swapped[8] = {0}; |
585 | qToBigEndian<quint64>(src: payloadSize, dest: swapped); |
586 | QTest::newRow(dataTag: "Overlong 64-bit size field; should be 7-bit" ) |
587 | << wireRep.left(len: 1).append(c: bigpayloadIndicator) |
588 | .append(s: reinterpret_cast<char *>(swapped), len: 8) |
589 | << QWebSocketProtocol::CloseCodeProtocolError; |
590 | |
591 | payloadSize = 256; |
592 | qToBigEndian<quint64>(src: payloadSize, dest: swapped); |
593 | QTest::newRow(dataTag: "Overlong 64-bit size field; should be 16-bit" ) |
594 | << wireRep.left(len: 1).append(c: bigpayloadIndicator) |
595 | .append(s: reinterpret_cast<char *>(swapped), len: 8) |
596 | << QWebSocketProtocol::CloseCodeProtocolError; |
597 | } |
598 | } |
599 | |
600 | void tst_WebSocketFrame::tst_malformedFrames() |
601 | { |
602 | QFETCH(QByteArray, payload); |
603 | QFETCH(QWebSocketProtocol::CloseCode, expectedError); |
604 | |
605 | QBuffer buffer; |
606 | buffer.setData(payload); |
607 | buffer.open(openMode: QIODevice::ReadOnly); |
608 | QWebSocketFrame frame; |
609 | frame.readFrame(pIoDevice: &buffer); |
610 | buffer.close(); |
611 | |
612 | QVERIFY(!frame.isValid()); |
613 | QCOMPARE(frame.closeCode(), expectedError); |
614 | } |
615 | |
616 | QTEST_MAIN(tst_WebSocketFrame) |
617 | |
618 | #include "tst_websocketframe.moc" |
619 | |
620 | |