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:LGPL21$
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 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
26 ** In addition, as a special exception, Digia gives you certain additional
27 ** rights. These rights are described in the Digia Qt LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
32 ****************************************************************************/
33 #include <QtTest/QtTest>
34 #include <QtTest/qtestcase.h>
39 #include "private/qwebsocketframe_p.h"
40 #include "private/qwebsocketprotocol_p.h"
41 #include "qwebsocketprotocol.h"
45 Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
46 Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode)
49 * \brief class FrameHelper is used to encode a single frame.
58 QByteArray wireRepresentation();
60 void setRsv1(int value) { m_rsv1 = value; }
61 void setRsv2(int value) { m_rsv2 = value; }
62 void setRsv3(int value) { m_rsv3 = value; }
63 void setMask(quint32 mask) { m_mask = mask; }
64 void setOpCode(QWebSocketProtocol::OpCode opCode) { m_opCode = opCode; }
65 void setPayload(const QByteArray &payload) { m_payload = payload; }
66 void setFinalFrame(bool isFinal) { m_isFinalFrame = isFinal; }
73 QWebSocketProtocol::OpCode m_opCode;
78 FrameHelper::FrameHelper() :
79 m_rsv1(0), m_rsv2(0), m_rsv3(0),
80 m_mask(0), m_opCode(QWebSocketProtocol::OpCodeReserved3),
81 m_payload(), m_isFinalFrame(false)
84 QByteArray FrameHelper::wireRepresentation()
88 quint64 payloadLength = m_payload.length();
91 byte = static_cast<quint8>((m_opCode & 0x0F) | (m_isFinalFrame ? 0x80 : 0x00)); //FIN, opcode
93 byte |= static_cast<quint8>(((m_rsv1 & 0x01) << 6) | ((m_rsv2 & 0x01) << 5) |
94 ((m_rsv3 & 0x01) << 4));
95 wireRep.append(static_cast<char>(byte));
102 if (payloadLength <= 125)
104 byte |= static_cast<quint8>(payloadLength);
105 wireRep.append(static_cast<char>(byte));
107 else if (payloadLength <= 0xFFFFU)
110 wireRep.append(static_cast<char>(byte));
111 quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
112 wireRep.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
117 wireRep.append(static_cast<char>(byte));
118 quint64 swapped = qToBigEndian<quint64>(payloadLength);
119 wireRep.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
124 wireRep.append(static_cast<const char *>(static_cast<const void *>(&m_mask)),
127 QByteArray tmpData = m_payload;
131 QWebSocketProtocol::mask(&tmpData, m_mask);
133 wireRep.append(tmpData);
137 class tst_WebSocketFrame : public QObject
142 tst_WebSocketFrame();
146 void cleanupTestCase();
150 void tst_initialization();
151 void tst_copyConstructorAndAssignment();
153 void tst_goodFrames_data();
154 void tst_goodFrames();
156 void tst_invalidFrames_data();
157 void tst_invalidFrames();
159 void tst_malformedFrames_data();
160 void tst_malformedFrames();
163 tst_WebSocketFrame::tst_WebSocketFrame()
166 void tst_WebSocketFrame::initTestCase()
170 void tst_WebSocketFrame::cleanupTestCase()
173 void tst_WebSocketFrame::init()
175 qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode");
176 qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode");
179 void tst_WebSocketFrame::cleanup()
183 void tst_WebSocketFrame::tst_initialization()
185 QWebSocketFrame frame;
186 QVERIFY(!frame.isValid());
187 QCOMPARE(frame.payload().length(), 0);
190 void tst_WebSocketFrame::tst_copyConstructorAndAssignment()
192 FrameHelper frameHelper;
193 frameHelper.setRsv1(0);
194 frameHelper.setRsv2(0);
195 frameHelper.setRsv3(0);
196 frameHelper.setFinalFrame(true);
197 frameHelper.setMask(1234u);
198 frameHelper.setOpCode(QWebSocketProtocol::OpCodeBinary);
199 frameHelper.setPayload(QByteArrayLiteral("12345"));
201 QByteArray payload = frameHelper.wireRepresentation();
202 QBuffer buffer(&payload);
203 buffer.open(QIODevice::ReadOnly);
205 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
209 QWebSocketFrame other(frame);
210 QCOMPARE(other.closeCode(), frame.closeCode());
211 QCOMPARE(other.closeReason(), frame.closeReason());
212 QCOMPARE(other.hasMask(), frame.hasMask());
213 QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
214 QCOMPARE(other.isControlFrame(), frame.isControlFrame());
215 QCOMPARE(other.isDataFrame(), frame.isDataFrame());
216 QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
217 QCOMPARE(other.isValid(), frame.isValid());
218 QCOMPARE(other.mask(), frame.mask());
219 QCOMPARE(other.opCode(), frame.opCode());
220 QCOMPARE(other.payload(), frame.payload());
221 QCOMPARE(other.rsv1(), frame.rsv1());
222 QCOMPARE(other.rsv2(), frame.rsv2());
223 QCOMPARE(other.rsv3(), frame.rsv3());
226 QWebSocketFrame other;
228 QCOMPARE(other.closeCode(), frame.closeCode());
229 QCOMPARE(other.closeReason(), frame.closeReason());
230 QCOMPARE(other.hasMask(), frame.hasMask());
231 QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
232 QCOMPARE(other.isControlFrame(), frame.isControlFrame());
233 QCOMPARE(other.isDataFrame(), frame.isDataFrame());
234 QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
235 QCOMPARE(other.isValid(), frame.isValid());
236 QCOMPARE(other.mask(), frame.mask());
237 QCOMPARE(other.opCode(), frame.opCode());
238 QCOMPARE(other.payload(), frame.payload());
239 QCOMPARE(other.rsv1(), frame.rsv1());
240 QCOMPARE(other.rsv2(), frame.rsv2());
241 QCOMPARE(other.rsv3(), frame.rsv3());
245 void tst_WebSocketFrame::tst_goodFrames_data()
247 QTest::addColumn<int>("rsv1");
248 QTest::addColumn<int>("rsv2");
249 QTest::addColumn<int>("rsv3");
250 QTest::addColumn<quint32>("mask");
251 QTest::addColumn<QWebSocketProtocol::OpCode>("opCode");
252 QTest::addColumn<bool>("isFinal");
253 QTest::addColumn<QByteArray>("payload");
254 QTest::addColumn<bool>("isControlFrame");
255 QTest::addColumn<bool>("isDataFrame");
256 QTest::addColumn<bool>("isContinuationFrame");
258 QTest::newRow("Non masked final text frame with small payload")
260 << 0U << QWebSocketProtocol::OpCodeText
261 << true << QStringLiteral("Hello world!").toUtf8()
262 << false << true << false;
263 QTest::newRow("Non masked final binary frame with small payload")
265 << 0U << QWebSocketProtocol::OpCodeBinary
266 << true << QByteArrayLiteral("\x00\x01\x02\x03\x04")
267 << false << true << false;
268 QTest::newRow("Non masked final text frame with no payload")
270 << 0U << QWebSocketProtocol::OpCodeText
271 << true << QByteArray()
272 << false << true << false;
273 QTest::newRow("Non masked final binary frame with no payload")
275 << 0U << QWebSocketProtocol::OpCodeBinary
276 << true << QByteArray()
277 << false << true << false;
279 QTest::newRow("Non masked final close frame with small payload")
281 << 0U << QWebSocketProtocol::OpCodeClose
282 << true << QStringLiteral("Hello world!").toUtf8()
283 << true << false << false;
284 QTest::newRow("Non masked final close frame with no payload")
286 << 0U << QWebSocketProtocol::OpCodeClose
287 << true << QByteArray()
288 << true << false << false;
289 QTest::newRow("Non masked final ping frame with small payload")
291 << 0U << QWebSocketProtocol::OpCodePing
292 << true << QStringLiteral("Hello world!").toUtf8()
293 << true << false << false;
294 QTest::newRow("Non masked final pong frame with no payload")
296 << 0U << QWebSocketProtocol::OpCodePong
297 << true << QByteArray()
298 << true << false << false;
300 QTest::newRow("Non masked final continuation frame with small payload")
302 << 0U << QWebSocketProtocol::OpCodeContinue
303 << true << QStringLiteral("Hello world!").toUtf8()
304 << false << true << true;
305 QTest::newRow("Non masked non-final continuation frame with small payload")
307 << 0U << QWebSocketProtocol::OpCodeContinue
308 << false << QStringLiteral("Hello world!").toUtf8()
309 << false << true << true;
312 void tst_WebSocketFrame::tst_goodFrames()
317 QFETCH(quint32, mask);
318 QFETCH(QWebSocketProtocol::OpCode, opCode);
319 QFETCH(bool, isFinal);
320 QFETCH(QByteArray, payload);
321 QFETCH(bool, isControlFrame);
322 QFETCH(bool, isDataFrame);
323 QFETCH(bool, isContinuationFrame);
326 helper.setRsv1(rsv1);
327 helper.setRsv2(rsv2);
328 helper.setRsv3(rsv3);
329 helper.setMask(mask);
330 helper.setOpCode(opCode);
331 helper.setFinalFrame(isFinal);
332 helper.setPayload(payload);
334 QByteArray wireRepresentation = helper.wireRepresentation();
336 buffer.setData(wireRepresentation);
337 buffer.open(QIODevice::ReadOnly);
338 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
340 QVERIFY(frame.isValid());
341 QCOMPARE(frame.rsv1(), rsv1);
342 QCOMPARE(frame.rsv2(), rsv2);
343 QCOMPARE(frame.rsv3(), rsv3);
344 QCOMPARE(frame.hasMask(), (mask != 0));
345 QCOMPARE(frame.opCode(), opCode);
346 QCOMPARE(frame.isFinalFrame(), isFinal);
347 QCOMPARE(frame.isControlFrame(), isControlFrame);
348 QCOMPARE(frame.isDataFrame(), isDataFrame);
349 QCOMPARE(frame.isContinuationFrame(), isContinuationFrame);
350 QCOMPARE(frame.payload().length(), payload.length());
351 QCOMPARE(frame.payload(), payload);
354 void tst_WebSocketFrame::tst_invalidFrames_data()
356 QTest::addColumn<int>("rsv1");
357 QTest::addColumn<int>("rsv2");
358 QTest::addColumn<int>("rsv3");
359 QTest::addColumn<quint32>("mask");
360 QTest::addColumn<QWebSocketProtocol::OpCode>("opCode");
361 QTest::addColumn<bool>("isFinal");
362 QTest::addColumn<QByteArray>("payload");
363 QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedError");
365 QTest::newRow("RSV1 != 0")
367 << 0U << QWebSocketProtocol::OpCodeText
368 << true << QStringLiteral("Hello world!").toUtf8()
369 << QWebSocketProtocol::CloseCodeProtocolError;
370 QTest::newRow("RSV2 != 0")
372 << 0U << QWebSocketProtocol::OpCodeText
373 << true << QStringLiteral("Hello world!").toUtf8()
374 << QWebSocketProtocol::CloseCodeProtocolError;
375 QTest::newRow("RSV3 != 0")
377 << 0U << QWebSocketProtocol::OpCodeText
378 << true << QStringLiteral("Hello world!").toUtf8()
379 << QWebSocketProtocol::CloseCodeProtocolError;
380 QTest::newRow("RSV1 != 0 and RSV2 != 0")
382 << 0U << QWebSocketProtocol::OpCodeText
383 << true << QStringLiteral("Hello world!").toUtf8()
384 << QWebSocketProtocol::CloseCodeProtocolError;
385 QTest::newRow("RSV1 != 0 and RSV3 != 0")
387 << 0U << QWebSocketProtocol::OpCodeText
388 << true << QStringLiteral("Hello world!").toUtf8()
389 << QWebSocketProtocol::CloseCodeProtocolError;
390 QTest::newRow("RSV2 != 0 and RSV3 != 0")
392 << 0U << QWebSocketProtocol::OpCodeText
393 << true << QStringLiteral("Hello world!").toUtf8()
394 << QWebSocketProtocol::CloseCodeProtocolError;
396 QTest::newRow("Reserved OpCode 3")
398 << 0U << QWebSocketProtocol::OpCodeReserved3
399 << true << QStringLiteral("Hello world!").toUtf8()
400 << QWebSocketProtocol::CloseCodeProtocolError;
401 QTest::newRow("Reserved OpCode 4")
403 << 0U << QWebSocketProtocol::OpCodeReserved4
404 << true << QStringLiteral("Hello world!").toUtf8()
405 << QWebSocketProtocol::CloseCodeProtocolError;
406 QTest::newRow("Reserved OpCode 5")
408 << 0U << QWebSocketProtocol::OpCodeReserved5
409 << true << QStringLiteral("Hello world!").toUtf8()
410 << QWebSocketProtocol::CloseCodeProtocolError;
411 QTest::newRow("Reserved OpCode 6")
413 << 0U << QWebSocketProtocol::OpCodeReserved6
414 << true << QStringLiteral("Hello world!").toUtf8()
415 << QWebSocketProtocol::CloseCodeProtocolError;
416 QTest::newRow("Reserved OpCode 7")
418 << 0U << QWebSocketProtocol::OpCodeReserved7
419 << true << QStringLiteral("Hello world!").toUtf8()
420 << QWebSocketProtocol::CloseCodeProtocolError;
421 QTest::newRow("Reserved OpCode B")
423 << 0U << QWebSocketProtocol::OpCodeReservedB
424 << true << QStringLiteral("Hello world!").toUtf8()
425 << QWebSocketProtocol::CloseCodeProtocolError;
426 QTest::newRow("Reserved OpCode C")
428 << 0U << QWebSocketProtocol::OpCodeReservedC
429 << true << QStringLiteral("Hello world!").toUtf8()
430 << QWebSocketProtocol::CloseCodeProtocolError;
431 QTest::newRow("Reserved OpCode D")
433 << 0U << QWebSocketProtocol::OpCodeReservedD
434 << true << QStringLiteral("Hello world!").toUtf8()
435 << QWebSocketProtocol::CloseCodeProtocolError;
436 QTest::newRow("Reserved OpCode E")
438 << 0U << QWebSocketProtocol::OpCodeReservedE
439 << true << QStringLiteral("Hello world!").toUtf8()
440 << QWebSocketProtocol::CloseCodeProtocolError;
441 QTest::newRow("Reserved OpCode F")
443 << 0U << QWebSocketProtocol::OpCodeReservedF
444 << true << QStringLiteral("Hello world!").toUtf8()
445 << QWebSocketProtocol::CloseCodeProtocolError;
447 QTest::newRow("Close Frame with payload > 125 bytes")
449 << 0U << QWebSocketProtocol::OpCodeClose
450 << true << QString(126, 'a').toUtf8()
451 << QWebSocketProtocol::CloseCodeProtocolError;
452 QTest::newRow("Non-final Close Frame")
454 << 0U << QWebSocketProtocol::OpCodeClose
455 << false << QString(126, 'a').toUtf8()
456 << QWebSocketProtocol::CloseCodeProtocolError;
457 QTest::newRow("Ping Frame with payload > 125 bytes")
459 << 0U << QWebSocketProtocol::OpCodePing
460 << true << QString(126, 'a').toUtf8()
461 << QWebSocketProtocol::CloseCodeProtocolError;
462 QTest::newRow("Non-final Ping Frame")
464 << 0U << QWebSocketProtocol::OpCodePing
465 << false << QString(126, 'a').toUtf8()
466 << QWebSocketProtocol::CloseCodeProtocolError;
467 QTest::newRow("Pong Frame with payload > 125 bytes")
469 << 0U << QWebSocketProtocol::OpCodePong
470 << true << QString(126, 'a').toUtf8()
471 << QWebSocketProtocol::CloseCodeProtocolError;
472 QTest::newRow("Non-final Pong Frame")
474 << 0U << QWebSocketProtocol::OpCodePong
475 << false << QString(126, 'a').toUtf8()
476 << QWebSocketProtocol::CloseCodeProtocolError;
479 void tst_WebSocketFrame::tst_invalidFrames()
484 QFETCH(quint32, mask);
485 QFETCH(QWebSocketProtocol::OpCode, opCode);
486 QFETCH(bool, isFinal);
487 QFETCH(QByteArray, payload);
488 QFETCH(QWebSocketProtocol::CloseCode, expectedError);
491 helper.setRsv1(rsv1);
492 helper.setRsv2(rsv2);
493 helper.setRsv3(rsv3);
494 helper.setMask(mask);
495 helper.setOpCode(opCode);
496 helper.setFinalFrame(isFinal);
497 helper.setPayload(payload);
499 QByteArray wireRepresentation = helper.wireRepresentation();
501 buffer.setData(wireRepresentation);
502 buffer.open(QIODevice::ReadOnly);
503 QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
506 QVERIFY(!frame.isValid());
507 QCOMPARE(frame.closeCode(), expectedError);
512 * Incomplete or overly large frames
513 * Payload must be crafted manually
515 QTest::newRow("Frame Too Big")
517 << 0U << QWebSocketProtocol::OpCodeText
518 << true << QString(MAX_FRAME_SIZE_IN_BYTES + 1, 'a').toUtf8()
519 << QWebSocketProtocol::CloseCodeTooMuchData;
522 void tst_WebSocketFrame::tst_malformedFrames_data()
524 QTest::addColumn<QByteArray>("payload");
525 QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedError");
528 QTest::newRow("No data") << QByteArray() << QWebSocketProtocol::CloseCodeGoingAway;
534 helper.setOpCode(QWebSocketProtocol::OpCodeText);
535 helper.setFinalFrame(true);
536 helper.setPayload(QString(10, 'a').toUtf8());
537 QByteArray wireRep = helper.wireRepresentation();
540 //header + payload should be 12 bytes for non-masked payloads < 126 bytes
541 for (int i = 1; i < 12; ++i)
543 QTest::newRow(QStringLiteral("Header too small - %1 byte(s)").arg(i).toLatin1().constData())
545 << QWebSocketProtocol::CloseCodeGoingAway;
549 const char bigpayloadIndicator = char(127);
550 const quint64 payloadSize = MAX_FRAME_SIZE_IN_BYTES + 1;
551 uchar swapped[8] = {0};
552 qToBigEndian<quint64>(payloadSize, swapped);
553 QTest::newRow("Frame too big")
554 << wireRep.left(1).append(bigpayloadIndicator)
555 .append(reinterpret_cast<char *>(swapped), 8)
556 << QWebSocketProtocol::CloseCodeTooMuchData;
560 const char bigpayloadIndicator = char(127);
561 quint64 payloadSize = quint64(1) << 63;
562 uchar swapped[8] = {0};
563 qToBigEndian<quint64>(payloadSize, swapped);
564 QTest::newRow("Highest bit of payload length is set")
565 << wireRep.left(1).append(bigpayloadIndicator)
566 .append(reinterpret_cast<char *>(swapped), 8)
567 << QWebSocketProtocol::CloseCodeProtocolError;
570 qToBigEndian<quint64>(payloadSize, swapped);
571 QTest::newRow("Overlong 64-bit size field; should be 16-bit")
572 << wireRep.left(1).append(bigpayloadIndicator)
573 .append(reinterpret_cast<char *>(swapped), 8)
574 << QWebSocketProtocol::CloseCodeProtocolError;
576 //overlong size field
578 const char largepayloadIndicator = char(126);
579 const quint16 payloadSize = 120;
580 uchar swapped[2] = {0};
581 qToBigEndian<quint16>(payloadSize, swapped);
582 QTest::newRow("Overlong 16-bit size field")
583 << wireRep.left(1).append(largepayloadIndicator)
584 .append(reinterpret_cast<char *>(swapped), 2)
585 << QWebSocketProtocol::CloseCodeProtocolError;
588 const char bigpayloadIndicator = char(127);
589 quint64 payloadSize = 120;
590 uchar swapped[8] = {0};
591 qToBigEndian<quint64>(payloadSize, swapped);
592 QTest::newRow("Overlong 64-bit size field; should be 7-bit")
593 << wireRep.left(1).append(bigpayloadIndicator)
594 .append(reinterpret_cast<char *>(swapped), 8)
595 << QWebSocketProtocol::CloseCodeProtocolError;
598 qToBigEndian<quint64>(payloadSize, swapped);
599 QTest::newRow("Overlong 64-bit size field; should be 16-bit")
600 << wireRep.left(1).append(bigpayloadIndicator)
601 .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"