1 /****************************************************************************
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
41 #include <QtTest/QtTest>
42 #include <QtTest/qtestcase.h>
47 #include "private/qwebsocketframe_p.h"
48 #include "private/qwebsocketprotocol_p.h"
49 #include "qwebsocketprotocol.h"
53 Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
54 Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
57 * \brief class FrameHelper is used to encode a single frame.
66 QByteArray wireRepresentation();
68 void setRsv1(int value) { m_rsv1 = value; }
69 void setRsv2(int value) { m_rsv2 = value; }
70 void setRsv3(int value) { m_rsv3 = value; }
71 void setMask(quint32 mask) { m_mask = mask; }
72 void setOpCode(QWebSocketProtocol::OpCode opCode) { m_opCode = opCode; }
73 void setPayload(const QByteArray &payload) { m_payload = payload; }
74 void setFinalFrame(bool isFinal) { m_isFinalFrame = isFinal; }
81 QWebSocketProtocol::OpCode m_opCode;
86 FrameHelper::FrameHelper() :
87 m_rsv1(0), m_rsv2(0), m_rsv3(0),
88 m_mask(0), m_opCode(QWebSocketProtocol::OpCodeReserved3),
89 m_payload(), m_isFinalFrame(false)
92 QByteArray FrameHelper::wireRepresentation()
96 quint64 payloadLength = m_payload.length();
99 byte = static_cast<quint8>((m_opCode & 0x0F) | (m_isFinalFrame ? 0x80 : 0x00)); //FIN, opcode
101 byte |= static_cast<quint8>(((m_rsv1 & 0x01) << 6) | ((m_rsv2 & 0x01) << 5) | ((m_rsv3 & 0x01) << 4));
102 wireRep.append(static_cast<char>(byte));
109 if (payloadLength <= 125)
111 byte |= static_cast<quint8>(payloadLength);
112 wireRep.append(static_cast<char>(byte));
114 else if (payloadLength <= 0xFFFFU)
117 wireRep.append(static_cast<char>(byte));
118 quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
119 wireRep.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
124 wireRep.append(static_cast<char>(byte));
125 quint64 swapped = qToBigEndian<quint64>(payloadLength);
126 wireRep.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
131 wireRep.append(static_cast<const char *>(static_cast<const void *>(&m_mask)), sizeof(quint32));
133 QByteArray tmpData = m_payload;
137 QWebSocketProtocol::mask(&tmpData, m_mask);
139 wireRep.append(tmpData);
143 class tst_WebSocketFrame : public QObject
148 tst_WebSocketFrame();
152 void cleanupTestCase();
156 void tst_initialization();
157 void tst_copyConstructorAndAssignment();
159 void tst_goodFrames_data();
160 void tst_goodFrames();
162 void tst_invalidFrames_data();
163 void tst_invalidFrames();
165 void tst_malformedFrames_data();
166 void tst_malformedFrames();
169 tst_WebSocketFrame::tst_WebSocketFrame()
172 void tst_WebSocketFrame::initTestCase()
176 void tst_WebSocketFrame::cleanupTestCase()
179 void tst_WebSocketFrame::init()
181 qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode");
182 qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
185 void tst_WebSocketFrame::cleanup()
189 void tst_WebSocketFrame::tst_initialization()
191 QWebSocketFrame frame;
192 QVERIFY(!frame.isValid());
193 QCOMPARE(frame.payload().length(), 0);
196 void tst_WebSocketFrame::tst_copyConstructorAndAssignment()
198 FrameHelper frameHelper;
199 frameHelper.setRsv1(0);
200 frameHelper.setRsv2(0);
201 frameHelper.setRsv3(0);
202 frameHelper.setFinalFrame(true);
203 frameHelper.setMask(1234u);
204 frameHelper.setOpCode(QWebSocketProtocol::OpCodeBinary);
205 frameHelper.setPayload(QByteArray("12345"));
207 QByteArray payload = frameHelper.wireRepresentation();
208 QBuffer buffer(&payload);
209 buffer.open(QIODevice::ReadOnly);
211 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
215 QWebSocketFrame other(frame);
216 QCOMPARE(other.closeCode(), frame.closeCode());
217 QCOMPARE(other.closeReason(), frame.closeReason());
218 QCOMPARE(other.hasMask(), frame.hasMask());
219 QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
220 QCOMPARE(other.isControlFrame(), frame.isControlFrame());
221 QCOMPARE(other.isDataFrame(), frame.isDataFrame());
222 QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
223 QCOMPARE(other.isValid(), frame.isValid());
224 QCOMPARE(other.mask(), frame.mask());
225 QCOMPARE(other.opCode(), frame.opCode());
226 QCOMPARE(other.payload(), frame.payload());
227 QCOMPARE(other.rsv1(), frame.rsv1());
228 QCOMPARE(other.rsv2(), frame.rsv2());
229 QCOMPARE(other.rsv3(), frame.rsv3());
232 QWebSocketFrame other;
234 QCOMPARE(other.closeCode(), frame.closeCode());
235 QCOMPARE(other.closeReason(), frame.closeReason());
236 QCOMPARE(other.hasMask(), frame.hasMask());
237 QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
238 QCOMPARE(other.isControlFrame(), frame.isControlFrame());
239 QCOMPARE(other.isDataFrame(), frame.isDataFrame());
240 QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
241 QCOMPARE(other.isValid(), frame.isValid());
242 QCOMPARE(other.mask(), frame.mask());
243 QCOMPARE(other.opCode(), frame.opCode());
244 QCOMPARE(other.payload(), frame.payload());
245 QCOMPARE(other.rsv1(), frame.rsv1());
246 QCOMPARE(other.rsv2(), frame.rsv2());
247 QCOMPARE(other.rsv3(), frame.rsv3());
251 void tst_WebSocketFrame::tst_goodFrames_data()
253 QTest::addColumn<int>("rsv1");
254 QTest::addColumn<int>("rsv2");
255 QTest::addColumn<int>("rsv3");
256 QTest::addColumn<quint32>("mask");
257 QTest::addColumn<QWebSocketProtocol::OpCode>("opCode");
258 QTest::addColumn<bool>("isFinal");
259 QTest::addColumn<QByteArray>("payload");
260 QTest::addColumn<bool>("isControlFrame");
261 QTest::addColumn<bool>("isDataFrame");
262 QTest::addColumn<bool>("isContinuationFrame");
264 QTest::newRow("Non masked final text frame with small payload")
266 << 0U << QWebSocketProtocol::OpCodeText
267 << true << QStringLiteral("Hello world!").toUtf8()
268 << false << true << false;
269 QTest::newRow("Non masked final binary frame with small payload")
271 << 0U << QWebSocketProtocol::OpCodeBinary
272 << true << QByteArrayLiteral("\x00\x01\x02\x03\x04")
273 << false << true << false;
274 QTest::newRow("Non masked final text frame with no payload")
276 << 0U << QWebSocketProtocol::OpCodeText
277 << true << QByteArray()
278 << false << true << false;
279 QTest::newRow("Non masked final binary frame with no payload")
281 << 0U << QWebSocketProtocol::OpCodeBinary
282 << true << QByteArray()
283 << false << true << false;
285 QTest::newRow("Non masked final close frame with small payload")
287 << 0U << QWebSocketProtocol::OpCodeClose
288 << true << QStringLiteral("Hello world!").toUtf8()
289 << true << false << false;
290 QTest::newRow("Non masked final close frame with no payload")
292 << 0U << QWebSocketProtocol::OpCodeClose
293 << true << QByteArray()
294 << true << false << false;
295 QTest::newRow("Non masked final ping frame with small payload")
297 << 0U << QWebSocketProtocol::OpCodePing
298 << true << QStringLiteral("Hello world!").toUtf8()
299 << true << false << false;
300 QTest::newRow("Non masked final pong frame with no payload")
302 << 0U << QWebSocketProtocol::OpCodePong
303 << true << QByteArray()
304 << true << false << false;
306 QTest::newRow("Non masked final continuation frame with small payload")
308 << 0U << QWebSocketProtocol::OpCodeContinue
309 << true << QStringLiteral("Hello world!").toUtf8()
310 << false << true << true;
311 QTest::newRow("Non masked non-final continuation frame with small payload")
313 << 0U << QWebSocketProtocol::OpCodeContinue
314 << false << QStringLiteral("Hello world!").toUtf8()
315 << false << true << true;
318 void tst_WebSocketFrame::tst_goodFrames()
323 QFETCH(quint32, mask);
324 QFETCH(QWebSocketProtocol::OpCode, opCode);
325 QFETCH(bool, isFinal);
326 QFETCH(QByteArray, payload);
327 QFETCH(bool, isControlFrame);
328 QFETCH(bool, isDataFrame);
329 QFETCH(bool, isContinuationFrame);
332 helper.setRsv1(rsv1);
333 helper.setRsv2(rsv2);
334 helper.setRsv3(rsv3);
335 helper.setMask(mask);
336 helper.setOpCode(opCode);
337 helper.setFinalFrame(isFinal);
338 helper.setPayload(payload);
340 QByteArray wireRepresentation = helper.wireRepresentation();
342 buffer.setData(wireRepresentation);
343 buffer.open(QIODevice::ReadOnly);
344 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
346 QVERIFY(frame.isValid());
347 QCOMPARE(frame.rsv1(), rsv1);
348 QCOMPARE(frame.rsv2(), rsv2);
349 QCOMPARE(frame.rsv3(), rsv3);
350 QCOMPARE(frame.hasMask(), (mask != 0));
351 QCOMPARE(frame.opCode(), opCode);
352 QCOMPARE(frame.isFinalFrame(), isFinal);
353 QCOMPARE(frame.isControlFrame(), isControlFrame);
354 QCOMPARE(frame.isDataFrame(), isDataFrame);
355 QCOMPARE(frame.isContinuationFrame(), isContinuationFrame);
356 QCOMPARE(frame.payload().length(), payload.length());
357 QCOMPARE(frame.payload(), payload);
360 void tst_WebSocketFrame::tst_invalidFrames_data()
362 QTest::addColumn<int>("rsv1");
363 QTest::addColumn<int>("rsv2");
364 QTest::addColumn<int>("rsv3");
365 QTest::addColumn<quint32>("mask");
366 QTest::addColumn<QWebSocketProtocol::OpCode>("opCode");
367 QTest::addColumn<bool>("isFinal");
368 QTest::addColumn<QByteArray>("payload");
369 QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedError");
371 QTest::newRow("RSV1 != 0")
373 << 0U << QWebSocketProtocol::OpCodeText
374 << true << QStringLiteral("Hello world!").toUtf8()
375 << QWebSocketProtocol::CloseCodeProtocolError;
376 QTest::newRow("RSV2 != 0")
378 << 0U << QWebSocketProtocol::OpCodeText
379 << true << QStringLiteral("Hello world!").toUtf8()
380 << QWebSocketProtocol::CloseCodeProtocolError;
381 QTest::newRow("RSV3 != 0")
383 << 0U << QWebSocketProtocol::OpCodeText
384 << true << QStringLiteral("Hello world!").toUtf8()
385 << QWebSocketProtocol::CloseCodeProtocolError;
386 QTest::newRow("RSV1 != 0 and RSV2 != 0")
388 << 0U << QWebSocketProtocol::OpCodeText
389 << true << QStringLiteral("Hello world!").toUtf8()
390 << QWebSocketProtocol::CloseCodeProtocolError;
391 QTest::newRow("RSV1 != 0 and RSV3 != 0")
393 << 0U << QWebSocketProtocol::OpCodeText
394 << true << QStringLiteral("Hello world!").toUtf8()
395 << QWebSocketProtocol::CloseCodeProtocolError;
396 QTest::newRow("RSV2 != 0 and RSV3 != 0")
398 << 0U << QWebSocketProtocol::OpCodeText
399 << true << QStringLiteral("Hello world!").toUtf8()
400 << QWebSocketProtocol::CloseCodeProtocolError;
402 QTest::newRow("Reserved OpCode 3")
404 << 0U << QWebSocketProtocol::OpCodeReserved3
405 << true << QStringLiteral("Hello world!").toUtf8()
406 << QWebSocketProtocol::CloseCodeProtocolError;
407 QTest::newRow("Reserved OpCode 4")
409 << 0U << QWebSocketProtocol::OpCodeReserved4
410 << true << QStringLiteral("Hello world!").toUtf8()
411 << QWebSocketProtocol::CloseCodeProtocolError;
412 QTest::newRow("Reserved OpCode 5")
414 << 0U << QWebSocketProtocol::OpCodeReserved5
415 << true << QStringLiteral("Hello world!").toUtf8()
416 << QWebSocketProtocol::CloseCodeProtocolError;
417 QTest::newRow("Reserved OpCode 6")
419 << 0U << QWebSocketProtocol::OpCodeReserved6
420 << true << QStringLiteral("Hello world!").toUtf8()
421 << QWebSocketProtocol::CloseCodeProtocolError;
422 QTest::newRow("Reserved OpCode 7")
424 << 0U << QWebSocketProtocol::OpCodeReserved7
425 << true << QStringLiteral("Hello world!").toUtf8()
426 << QWebSocketProtocol::CloseCodeProtocolError;
427 QTest::newRow("Reserved OpCode B")
429 << 0U << QWebSocketProtocol::OpCodeReservedB
430 << true << QStringLiteral("Hello world!").toUtf8()
431 << QWebSocketProtocol::CloseCodeProtocolError;
432 QTest::newRow("Reserved OpCode C")
434 << 0U << QWebSocketProtocol::OpCodeReservedC
435 << true << QStringLiteral("Hello world!").toUtf8()
436 << QWebSocketProtocol::CloseCodeProtocolError;
437 QTest::newRow("Reserved OpCode D")
439 << 0U << QWebSocketProtocol::OpCodeReservedD
440 << true << QStringLiteral("Hello world!").toUtf8()
441 << QWebSocketProtocol::CloseCodeProtocolError;
442 QTest::newRow("Reserved OpCode E")
444 << 0U << QWebSocketProtocol::OpCodeReservedE
445 << true << QStringLiteral("Hello world!").toUtf8()
446 << QWebSocketProtocol::CloseCodeProtocolError;
447 QTest::newRow("Reserved OpCode F")
449 << 0U << QWebSocketProtocol::OpCodeReservedF
450 << true << QStringLiteral("Hello world!").toUtf8()
451 << QWebSocketProtocol::CloseCodeProtocolError;
453 QTest::newRow("Close Frame with payload > 125 bytes")
455 << 0U << QWebSocketProtocol::OpCodeClose
456 << true << QString(126, 'a').toUtf8()
457 << QWebSocketProtocol::CloseCodeProtocolError;
458 QTest::newRow("Non-final Close Frame")
460 << 0U << QWebSocketProtocol::OpCodeClose
461 << false << QString(126, 'a').toUtf8()
462 << QWebSocketProtocol::CloseCodeProtocolError;
463 QTest::newRow("Ping Frame with payload > 125 bytes")
465 << 0U << QWebSocketProtocol::OpCodePing
466 << true << QString(126, 'a').toUtf8()
467 << QWebSocketProtocol::CloseCodeProtocolError;
468 QTest::newRow("Non-final Ping Frame")
470 << 0U << QWebSocketProtocol::OpCodePing
471 << false << QString(126, 'a').toUtf8()
472 << QWebSocketProtocol::CloseCodeProtocolError;
473 QTest::newRow("Pong Frame with payload > 125 bytes")
475 << 0U << QWebSocketProtocol::OpCodePong
476 << true << QString(126, 'a').toUtf8()
477 << QWebSocketProtocol::CloseCodeProtocolError;
478 QTest::newRow("Non-final Pong Frame")
480 << 0U << QWebSocketProtocol::OpCodePong
481 << false << QString(126, 'a').toUtf8()
482 << QWebSocketProtocol::CloseCodeProtocolError;
485 void tst_WebSocketFrame::tst_invalidFrames()
490 QFETCH(quint32, mask);
491 QFETCH(QWebSocketProtocol::OpCode, opCode);
492 QFETCH(bool, isFinal);
493 QFETCH(QByteArray, payload);
494 QFETCH(QWebSocketProtocol::CloseCode, expectedError);
497 helper.setRsv1(rsv1);
498 helper.setRsv2(rsv2);
499 helper.setRsv3(rsv3);
500 helper.setMask(mask);
501 helper.setOpCode(opCode);
502 helper.setFinalFrame(isFinal);
503 helper.setPayload(payload);
505 QByteArray wireRepresentation = helper.wireRepresentation();
507 buffer.setData(wireRepresentation);
508 buffer.open(QIODevice::ReadOnly);
509 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
512 QVERIFY(!frame.isValid());
513 QCOMPARE(frame.closeCode(), expectedError);
518 * Incomplete or overly large frames
519 * Payload must be crafted manually
521 QTest::newRow("Frame Too Big")
523 << 0U << QWebSocketProtocol::OpCodeText
524 << true << QString(MAX_FRAME_SIZE_IN_BYTES + 1, 'a').toUtf8()
525 << QWebSocketProtocol::CloseCodeTooMuchData;
528 void tst_WebSocketFrame::tst_malformedFrames_data()
530 QTest::addColumn<QByteArray>("payload");
531 QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedError");
534 QTest::newRow("No data") << QByteArray() << QWebSocketProtocol::CloseCodeGoingAway;
540 helper.setOpCode(QWebSocketProtocol::OpCodeText);
541 helper.setFinalFrame(true);
542 helper.setPayload(QString(10, 'a').toUtf8());
543 QByteArray wireRep = helper.wireRepresentation();
546 //header + payload should be 12 bytes for non-masked payloads < 126 bytes
547 for (int i = 1; i < 12; ++i)
549 QTest::newRow(QStringLiteral("Header too small - %1 byte(s)").arg(i).toLatin1().constData())
551 << QWebSocketProtocol::CloseCodeGoingAway;
555 const char bigpayloadIndicator = char(127);
556 const quint64 payloadSize = MAX_FRAME_SIZE_IN_BYTES + 1;
557 uchar swapped[8] = {0};
558 qToBigEndian<quint64>(payloadSize, swapped);
559 QTest::newRow("Frame too big")
560 << wireRep.left(1).append(bigpayloadIndicator).append(reinterpret_cast<char *>(swapped), 8)
561 << QWebSocketProtocol::CloseCodeTooMuchData;
565 const char bigpayloadIndicator = char(127);
566 quint64 payloadSize = quint64(1) << 63;
567 uchar swapped[8] = {0};
568 qToBigEndian<quint64>(payloadSize, swapped);
569 QTest::newRow("Highest bit of payload length is set")
570 << wireRep.left(1).append(bigpayloadIndicator).append(reinterpret_cast<char *>(swapped), 8)
571 << QWebSocketProtocol::CloseCodeProtocolError;
574 qToBigEndian<quint64>(payloadSize, swapped);
575 QTest::newRow("Overlong 64-bit size field; should be 16-bit")
576 << wireRep.left(1).append(bigpayloadIndicator).append(reinterpret_cast<char *>(swapped), 8)
577 << QWebSocketProtocol::CloseCodeProtocolError;
579 //overlong size field
581 const char largepayloadIndicator = char(126);
582 const quint16 payloadSize = 120;
583 uchar swapped[2] = {0};
584 qToBigEndian<quint16>(payloadSize, swapped);
585 QTest::newRow("Overlong 16-bit size field")
586 << wireRep.left(1).append(largepayloadIndicator).append(reinterpret_cast<char *>(swapped), 2)
587 << QWebSocketProtocol::CloseCodeProtocolError;
590 const char bigpayloadIndicator = char(127);
591 quint64 payloadSize = 120;
592 uchar swapped[8] = {0};
593 qToBigEndian<quint64>(payloadSize, swapped);
594 QTest::newRow("Overlong 64-bit size field; should be 7-bit")
595 << wireRep.left(1).append(bigpayloadIndicator).append(reinterpret_cast<char *>(swapped), 8)
596 << QWebSocketProtocol::CloseCodeProtocolError;
599 qToBigEndian<quint64>(payloadSize, swapped);
600 QTest::newRow("Overlong 64-bit size field; should be 16-bit")
601 << wireRep.left(1).append(bigpayloadIndicator).append(reinterpret_cast<char *>(swapped), 8)
602 << QWebSocketProtocol::CloseCodeProtocolError;
606 void tst_WebSocketFrame::tst_malformedFrames()
608 QFETCH(QByteArray, payload);
609 QFETCH(QWebSocketProtocol::CloseCode, expectedError);
612 buffer.setData(payload);
613 buffer.open(QIODevice::ReadOnly);
614 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
617 QVERIFY(!frame.isValid());
618 QCOMPARE(frame.closeCode(), expectedError);
621 QTEST_MAIN(tst_WebSocketFrame)
623 #include "tst_websocketframe.moc"