1 /****************************************************************************
3 ** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
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) |
102 ((m_rsv3 & 0x01) << 4));
103 wireRep.append(static_cast<char>(byte));
110 if (payloadLength <= 125)
112 byte |= static_cast<quint8>(payloadLength);
113 wireRep.append(static_cast<char>(byte));
115 else if (payloadLength <= 0xFFFFU)
118 wireRep.append(static_cast<char>(byte));
119 quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
120 wireRep.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
125 wireRep.append(static_cast<char>(byte));
126 quint64 swapped = qToBigEndian<quint64>(payloadLength);
127 wireRep.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
132 wireRep.append(static_cast<const char *>(static_cast<const void *>(&m_mask)),
135 QByteArray tmpData = m_payload;
139 QWebSocketProtocol::mask(&tmpData, m_mask);
141 wireRep.append(tmpData);
145 class tst_WebSocketFrame : public QObject
150 tst_WebSocketFrame();
154 void cleanupTestCase();
158 void tst_initialization();
159 void tst_copyConstructorAndAssignment();
161 void tst_goodFrames_data();
162 void tst_goodFrames();
164 void tst_invalidFrames_data();
165 void tst_invalidFrames();
167 void tst_malformedFrames_data();
168 void tst_malformedFrames();
171 tst_WebSocketFrame::tst_WebSocketFrame()
174 void tst_WebSocketFrame::initTestCase()
178 void tst_WebSocketFrame::cleanupTestCase()
181 void tst_WebSocketFrame::init()
183 qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode");
184 qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
187 void tst_WebSocketFrame::cleanup()
191 void tst_WebSocketFrame::tst_initialization()
193 QWebSocketFrame frame;
194 QVERIFY(!frame.isValid());
195 QCOMPARE(frame.payload().length(), 0);
198 void tst_WebSocketFrame::tst_copyConstructorAndAssignment()
200 FrameHelper frameHelper;
201 frameHelper.setRsv1(0);
202 frameHelper.setRsv2(0);
203 frameHelper.setRsv3(0);
204 frameHelper.setFinalFrame(true);
205 frameHelper.setMask(1234u);
206 frameHelper.setOpCode(QWebSocketProtocol::OpCodeBinary);
207 frameHelper.setPayload(QByteArrayLiteral("12345"));
209 QByteArray payload = frameHelper.wireRepresentation();
210 QBuffer buffer(&payload);
211 buffer.open(QIODevice::ReadOnly);
213 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
217 QWebSocketFrame other(frame);
218 QCOMPARE(other.closeCode(), frame.closeCode());
219 QCOMPARE(other.closeReason(), frame.closeReason());
220 QCOMPARE(other.hasMask(), frame.hasMask());
221 QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
222 QCOMPARE(other.isControlFrame(), frame.isControlFrame());
223 QCOMPARE(other.isDataFrame(), frame.isDataFrame());
224 QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
225 QCOMPARE(other.isValid(), frame.isValid());
226 QCOMPARE(other.mask(), frame.mask());
227 QCOMPARE(other.opCode(), frame.opCode());
228 QCOMPARE(other.payload(), frame.payload());
229 QCOMPARE(other.rsv1(), frame.rsv1());
230 QCOMPARE(other.rsv2(), frame.rsv2());
231 QCOMPARE(other.rsv3(), frame.rsv3());
234 QWebSocketFrame other;
236 QCOMPARE(other.closeCode(), frame.closeCode());
237 QCOMPARE(other.closeReason(), frame.closeReason());
238 QCOMPARE(other.hasMask(), frame.hasMask());
239 QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
240 QCOMPARE(other.isControlFrame(), frame.isControlFrame());
241 QCOMPARE(other.isDataFrame(), frame.isDataFrame());
242 QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
243 QCOMPARE(other.isValid(), frame.isValid());
244 QCOMPARE(other.mask(), frame.mask());
245 QCOMPARE(other.opCode(), frame.opCode());
246 QCOMPARE(other.payload(), frame.payload());
247 QCOMPARE(other.rsv1(), frame.rsv1());
248 QCOMPARE(other.rsv2(), frame.rsv2());
249 QCOMPARE(other.rsv3(), frame.rsv3());
253 void tst_WebSocketFrame::tst_goodFrames_data()
255 QTest::addColumn<int>("rsv1");
256 QTest::addColumn<int>("rsv2");
257 QTest::addColumn<int>("rsv3");
258 QTest::addColumn<quint32>("mask");
259 QTest::addColumn<QWebSocketProtocol::OpCode>("opCode");
260 QTest::addColumn<bool>("isFinal");
261 QTest::addColumn<QByteArray>("payload");
262 QTest::addColumn<bool>("isControlFrame");
263 QTest::addColumn<bool>("isDataFrame");
264 QTest::addColumn<bool>("isContinuationFrame");
266 QTest::newRow("Non masked final text frame with small payload")
268 << 0U << QWebSocketProtocol::OpCodeText
269 << true << QStringLiteral("Hello world!").toUtf8()
270 << false << true << false;
271 QTest::newRow("Non masked final binary frame with small payload")
273 << 0U << QWebSocketProtocol::OpCodeBinary
274 << true << QByteArrayLiteral("\x00\x01\x02\x03\x04")
275 << false << true << false;
276 QTest::newRow("Non masked final text frame with no payload")
278 << 0U << QWebSocketProtocol::OpCodeText
279 << true << QByteArray()
280 << false << true << false;
281 QTest::newRow("Non masked final binary frame with no payload")
283 << 0U << QWebSocketProtocol::OpCodeBinary
284 << true << QByteArray()
285 << false << true << false;
287 QTest::newRow("Non masked final close frame with small payload")
289 << 0U << QWebSocketProtocol::OpCodeClose
290 << true << QStringLiteral("Hello world!").toUtf8()
291 << true << false << false;
292 QTest::newRow("Non masked final close frame with no payload")
294 << 0U << QWebSocketProtocol::OpCodeClose
295 << true << QByteArray()
296 << true << false << false;
297 QTest::newRow("Non masked final ping frame with small payload")
299 << 0U << QWebSocketProtocol::OpCodePing
300 << true << QStringLiteral("Hello world!").toUtf8()
301 << true << false << false;
302 QTest::newRow("Non masked final pong frame with no payload")
304 << 0U << QWebSocketProtocol::OpCodePong
305 << true << QByteArray()
306 << true << false << false;
308 QTest::newRow("Non masked final continuation frame with small payload")
310 << 0U << QWebSocketProtocol::OpCodeContinue
311 << true << QStringLiteral("Hello world!").toUtf8()
312 << false << true << true;
313 QTest::newRow("Non masked non-final continuation frame with small payload")
315 << 0U << QWebSocketProtocol::OpCodeContinue
316 << false << QStringLiteral("Hello world!").toUtf8()
317 << false << true << true;
320 void tst_WebSocketFrame::tst_goodFrames()
325 QFETCH(quint32, mask);
326 QFETCH(QWebSocketProtocol::OpCode, opCode);
327 QFETCH(bool, isFinal);
328 QFETCH(QByteArray, payload);
329 QFETCH(bool, isControlFrame);
330 QFETCH(bool, isDataFrame);
331 QFETCH(bool, isContinuationFrame);
334 helper.setRsv1(rsv1);
335 helper.setRsv2(rsv2);
336 helper.setRsv3(rsv3);
337 helper.setMask(mask);
338 helper.setOpCode(opCode);
339 helper.setFinalFrame(isFinal);
340 helper.setPayload(payload);
342 QByteArray wireRepresentation = helper.wireRepresentation();
344 buffer.setData(wireRepresentation);
345 buffer.open(QIODevice::ReadOnly);
346 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
348 QVERIFY(frame.isValid());
349 QCOMPARE(frame.rsv1(), rsv1);
350 QCOMPARE(frame.rsv2(), rsv2);
351 QCOMPARE(frame.rsv3(), rsv3);
352 QCOMPARE(frame.hasMask(), (mask != 0));
353 QCOMPARE(frame.opCode(), opCode);
354 QCOMPARE(frame.isFinalFrame(), isFinal);
355 QCOMPARE(frame.isControlFrame(), isControlFrame);
356 QCOMPARE(frame.isDataFrame(), isDataFrame);
357 QCOMPARE(frame.isContinuationFrame(), isContinuationFrame);
358 QCOMPARE(frame.payload().length(), payload.length());
359 QCOMPARE(frame.payload(), payload);
362 void tst_WebSocketFrame::tst_invalidFrames_data()
364 QTest::addColumn<int>("rsv1");
365 QTest::addColumn<int>("rsv2");
366 QTest::addColumn<int>("rsv3");
367 QTest::addColumn<quint32>("mask");
368 QTest::addColumn<QWebSocketProtocol::OpCode>("opCode");
369 QTest::addColumn<bool>("isFinal");
370 QTest::addColumn<QByteArray>("payload");
371 QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedError");
373 QTest::newRow("RSV1 != 0")
375 << 0U << QWebSocketProtocol::OpCodeText
376 << true << QStringLiteral("Hello world!").toUtf8()
377 << QWebSocketProtocol::CloseCodeProtocolError;
378 QTest::newRow("RSV2 != 0")
380 << 0U << QWebSocketProtocol::OpCodeText
381 << true << QStringLiteral("Hello world!").toUtf8()
382 << QWebSocketProtocol::CloseCodeProtocolError;
383 QTest::newRow("RSV3 != 0")
385 << 0U << QWebSocketProtocol::OpCodeText
386 << true << QStringLiteral("Hello world!").toUtf8()
387 << QWebSocketProtocol::CloseCodeProtocolError;
388 QTest::newRow("RSV1 != 0 and RSV2 != 0")
390 << 0U << QWebSocketProtocol::OpCodeText
391 << true << QStringLiteral("Hello world!").toUtf8()
392 << QWebSocketProtocol::CloseCodeProtocolError;
393 QTest::newRow("RSV1 != 0 and RSV3 != 0")
395 << 0U << QWebSocketProtocol::OpCodeText
396 << true << QStringLiteral("Hello world!").toUtf8()
397 << QWebSocketProtocol::CloseCodeProtocolError;
398 QTest::newRow("RSV2 != 0 and RSV3 != 0")
400 << 0U << QWebSocketProtocol::OpCodeText
401 << true << QStringLiteral("Hello world!").toUtf8()
402 << QWebSocketProtocol::CloseCodeProtocolError;
404 QTest::newRow("Reserved OpCode 3")
406 << 0U << QWebSocketProtocol::OpCodeReserved3
407 << true << QStringLiteral("Hello world!").toUtf8()
408 << QWebSocketProtocol::CloseCodeProtocolError;
409 QTest::newRow("Reserved OpCode 4")
411 << 0U << QWebSocketProtocol::OpCodeReserved4
412 << true << QStringLiteral("Hello world!").toUtf8()
413 << QWebSocketProtocol::CloseCodeProtocolError;
414 QTest::newRow("Reserved OpCode 5")
416 << 0U << QWebSocketProtocol::OpCodeReserved5
417 << true << QStringLiteral("Hello world!").toUtf8()
418 << QWebSocketProtocol::CloseCodeProtocolError;
419 QTest::newRow("Reserved OpCode 6")
421 << 0U << QWebSocketProtocol::OpCodeReserved6
422 << true << QStringLiteral("Hello world!").toUtf8()
423 << QWebSocketProtocol::CloseCodeProtocolError;
424 QTest::newRow("Reserved OpCode 7")
426 << 0U << QWebSocketProtocol::OpCodeReserved7
427 << true << QStringLiteral("Hello world!").toUtf8()
428 << QWebSocketProtocol::CloseCodeProtocolError;
429 QTest::newRow("Reserved OpCode B")
431 << 0U << QWebSocketProtocol::OpCodeReservedB
432 << true << QStringLiteral("Hello world!").toUtf8()
433 << QWebSocketProtocol::CloseCodeProtocolError;
434 QTest::newRow("Reserved OpCode C")
436 << 0U << QWebSocketProtocol::OpCodeReservedC
437 << true << QStringLiteral("Hello world!").toUtf8()
438 << QWebSocketProtocol::CloseCodeProtocolError;
439 QTest::newRow("Reserved OpCode D")
441 << 0U << QWebSocketProtocol::OpCodeReservedD
442 << true << QStringLiteral("Hello world!").toUtf8()
443 << QWebSocketProtocol::CloseCodeProtocolError;
444 QTest::newRow("Reserved OpCode E")
446 << 0U << QWebSocketProtocol::OpCodeReservedE
447 << true << QStringLiteral("Hello world!").toUtf8()
448 << QWebSocketProtocol::CloseCodeProtocolError;
449 QTest::newRow("Reserved OpCode F")
451 << 0U << QWebSocketProtocol::OpCodeReservedF
452 << true << QStringLiteral("Hello world!").toUtf8()
453 << QWebSocketProtocol::CloseCodeProtocolError;
455 QTest::newRow("Close Frame with payload > 125 bytes")
457 << 0U << QWebSocketProtocol::OpCodeClose
458 << true << QString(126, 'a').toUtf8()
459 << QWebSocketProtocol::CloseCodeProtocolError;
460 QTest::newRow("Non-final Close Frame")
462 << 0U << QWebSocketProtocol::OpCodeClose
463 << false << QString(126, 'a').toUtf8()
464 << QWebSocketProtocol::CloseCodeProtocolError;
465 QTest::newRow("Ping Frame with payload > 125 bytes")
467 << 0U << QWebSocketProtocol::OpCodePing
468 << true << QString(126, 'a').toUtf8()
469 << QWebSocketProtocol::CloseCodeProtocolError;
470 QTest::newRow("Non-final Ping Frame")
472 << 0U << QWebSocketProtocol::OpCodePing
473 << false << QString(126, 'a').toUtf8()
474 << QWebSocketProtocol::CloseCodeProtocolError;
475 QTest::newRow("Pong Frame with payload > 125 bytes")
477 << 0U << QWebSocketProtocol::OpCodePong
478 << true << QString(126, 'a').toUtf8()
479 << QWebSocketProtocol::CloseCodeProtocolError;
480 QTest::newRow("Non-final Pong Frame")
482 << 0U << QWebSocketProtocol::OpCodePong
483 << false << QString(126, 'a').toUtf8()
484 << QWebSocketProtocol::CloseCodeProtocolError;
487 void tst_WebSocketFrame::tst_invalidFrames()
492 QFETCH(quint32, mask);
493 QFETCH(QWebSocketProtocol::OpCode, opCode);
494 QFETCH(bool, isFinal);
495 QFETCH(QByteArray, payload);
496 QFETCH(QWebSocketProtocol::CloseCode, expectedError);
499 helper.setRsv1(rsv1);
500 helper.setRsv2(rsv2);
501 helper.setRsv3(rsv3);
502 helper.setMask(mask);
503 helper.setOpCode(opCode);
504 helper.setFinalFrame(isFinal);
505 helper.setPayload(payload);
507 QByteArray wireRepresentation = helper.wireRepresentation();
509 buffer.setData(wireRepresentation);
510 buffer.open(QIODevice::ReadOnly);
511 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
514 QVERIFY(!frame.isValid());
515 QCOMPARE(frame.closeCode(), expectedError);
520 * Incomplete or overly large frames
521 * Payload must be crafted manually
523 QTest::newRow("Frame Too Big")
525 << 0U << QWebSocketProtocol::OpCodeText
526 << true << QString(MAX_FRAME_SIZE_IN_BYTES + 1, 'a').toUtf8()
527 << QWebSocketProtocol::CloseCodeTooMuchData;
530 void tst_WebSocketFrame::tst_malformedFrames_data()
532 QTest::addColumn<QByteArray>("payload");
533 QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedError");
536 QTest::newRow("No data") << QByteArray() << QWebSocketProtocol::CloseCodeGoingAway;
542 helper.setOpCode(QWebSocketProtocol::OpCodeText);
543 helper.setFinalFrame(true);
544 helper.setPayload(QString(10, 'a').toUtf8());
545 QByteArray wireRep = helper.wireRepresentation();
548 //header + payload should be 12 bytes for non-masked payloads < 126 bytes
549 for (int i = 1; i < 12; ++i)
551 QTest::newRow(QStringLiteral("Header too small - %1 byte(s)").arg(i).toLatin1().constData())
553 << QWebSocketProtocol::CloseCodeGoingAway;
557 const char bigpayloadIndicator = char(127);
558 const quint64 payloadSize = MAX_FRAME_SIZE_IN_BYTES + 1;
559 uchar swapped[8] = {0};
560 qToBigEndian<quint64>(payloadSize, swapped);
561 QTest::newRow("Frame too big")
562 << wireRep.left(1).append(bigpayloadIndicator)
563 .append(reinterpret_cast<char *>(swapped), 8)
564 << QWebSocketProtocol::CloseCodeTooMuchData;
568 const char bigpayloadIndicator = char(127);
569 quint64 payloadSize = quint64(1) << 63;
570 uchar swapped[8] = {0};
571 qToBigEndian<quint64>(payloadSize, swapped);
572 QTest::newRow("Highest bit of payload length is set")
573 << wireRep.left(1).append(bigpayloadIndicator)
574 .append(reinterpret_cast<char *>(swapped), 8)
575 << QWebSocketProtocol::CloseCodeProtocolError;
578 qToBigEndian<quint64>(payloadSize, swapped);
579 QTest::newRow("Overlong 64-bit size field; should be 16-bit")
580 << wireRep.left(1).append(bigpayloadIndicator)
581 .append(reinterpret_cast<char *>(swapped), 8)
582 << QWebSocketProtocol::CloseCodeProtocolError;
584 //overlong size field
586 const char largepayloadIndicator = char(126);
587 const quint16 payloadSize = 120;
588 uchar swapped[2] = {0};
589 qToBigEndian<quint16>(payloadSize, swapped);
590 QTest::newRow("Overlong 16-bit size field")
591 << wireRep.left(1).append(largepayloadIndicator)
592 .append(reinterpret_cast<char *>(swapped), 2)
593 << QWebSocketProtocol::CloseCodeProtocolError;
596 const char bigpayloadIndicator = char(127);
597 quint64 payloadSize = 120;
598 uchar swapped[8] = {0};
599 qToBigEndian<quint64>(payloadSize, swapped);
600 QTest::newRow("Overlong 64-bit size field; should be 7-bit")
601 << wireRep.left(1).append(bigpayloadIndicator)
602 .append(reinterpret_cast<char *>(swapped), 8)
603 << QWebSocketProtocol::CloseCodeProtocolError;
606 qToBigEndian<quint64>(payloadSize, swapped);
607 QTest::newRow("Overlong 64-bit size field; should be 16-bit")
608 << wireRep.left(1).append(bigpayloadIndicator)
609 .append(reinterpret_cast<char *>(swapped), 8)
610 << QWebSocketProtocol::CloseCodeProtocolError;
614 void tst_WebSocketFrame::tst_malformedFrames()
616 QFETCH(QByteArray, payload);
617 QFETCH(QWebSocketProtocol::CloseCode, expectedError);
620 buffer.setData(payload);
621 buffer.open(QIODevice::ReadOnly);
622 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
625 QVERIFY(!frame.isValid());
626 QCOMPARE(frame.closeCode(), expectedError);
629 QTEST_MAIN(tst_WebSocketFrame)
631 #include "tst_websocketframe.moc"