diff options
author | Kurt Pattyn <pattyn.kurt@gmail.com> | 2013-10-13 15:30:33 +0200 |
---|---|---|
committer | Kurt Pattyn <pattyn.kurt@gmail.com> | 2013-10-13 16:58:45 +0200 |
commit | 4f884ef7b5588ee4f639a61a0a812d3efac90e81 (patch) | |
tree | 94add0e4ae3939347965e46c24c7e4b503c7afd8 /tests | |
parent | a0032d285f9148a9ae7f46e2d9c0e24a6fa84d0d (diff) | |
download | qtwebsockets-4f884ef7b5588ee4f639a61a0a812d3efac90e81.tar.gz |
Completed websocketframe unit tests
Replaced tab with space
Change-Id: Ifc47621069870123b5db458a1ba2bb0d66a5419d
Reviewed-by: Steven Ceuppens <steven.ceuppens@icloud.com>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/websocketframe/tst_websocketframe.cpp | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/tests/auto/websocketframe/tst_websocketframe.cpp b/tests/auto/websocketframe/tst_websocketframe.cpp index bd38c65..c53d02c 100644 --- a/tests/auto/websocketframe/tst_websocketframe.cpp +++ b/tests/auto/websocketframe/tst_websocketframe.cpp @@ -41,11 +41,104 @@ #include <QtTest/QtTest> #include <QtTest/qtestcase.h> #include <QDebug> +#include <QByteArray> +#include <QtEndian> #include "private/qwebsocketframe_p.h" +#include "qwebsocketprotocol.h" QT_USE_NAMESPACE +Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode) +Q_DECLARE_METATYPE(QWebSocketProtocol::OpCode) + +/*! + * \brief class FrameHelper is used to encode a single frame. + * + * \internal + */ +class FrameHelper +{ +public: + FrameHelper(); + + QByteArray wireRepresentation(); + + void setRsv1(int value) { m_rsv1 = value; } + void setRsv2(int value) { m_rsv2 = value; } + void setRsv3(int value) { m_rsv3 = value; } + void setMask(quint32 mask) { m_mask = mask; } + void setOpCode(QWebSocketProtocol::OpCode opCode) { m_opCode = opCode; } + void setPayload(const QByteArray &payload) { m_payload = payload; } + void setFinalFrame(bool isFinal) { m_isFinalFrame = isFinal; } + +private: + int m_rsv1; + int m_rsv2; + int m_rsv3; + quint32 m_mask; + QWebSocketProtocol::OpCode m_opCode; + QByteArray m_payload; + bool m_isFinalFrame; +}; + +FrameHelper::FrameHelper() : + m_rsv1(0), m_rsv2(0), m_rsv3(0), + m_mask(0), m_opCode(QWebSocketProtocol::OC_RESERVED_3), + m_payload(), m_isFinalFrame(false) +{} + +QByteArray FrameHelper::wireRepresentation() +{ + quint8 byte = 0x00; + QByteArray wireRep; + quint64 payloadLength = m_payload.length(); + + //FIN, opcode + byte = static_cast<quint8>((m_opCode & 0x0F) | (m_isFinalFrame ? 0x80 : 0x00)); //FIN, opcode + //RSV1-3 + byte |= static_cast<quint8>(((m_rsv1 & 0x01) << 6) | ((m_rsv2 & 0x01) << 5) | ((m_rsv3 & 0x01) << 4)); + wireRep.append(static_cast<char>(byte)); + + byte = 0x00; + if (m_mask != 0) + { + byte |= 0x80; + } + if (payloadLength <= 125) + { + byte |= static_cast<quint8>(payloadLength); + wireRep.append(static_cast<char>(byte)); + } + else if (payloadLength <= 0xFFFFU) + { + byte |= 126; + wireRep.append(static_cast<char>(byte)); + quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength)); + wireRep.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2); + } + else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) + { + byte |= 127; + wireRep.append(static_cast<char>(byte)); + quint64 swapped = qToBigEndian<quint64>(payloadLength); + wireRep.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8); + } + //Write mask + if (m_mask != 0) + { + wireRep.append(static_cast<const char *>(static_cast<const void *>(&m_mask)), sizeof(quint32)); + } + QByteArray tmpData = m_payload; + if (m_mask) + { + tmpData.detach(); + QWebSocketProtocol::mask(&tmpData, m_mask); + } + wireRep.append(tmpData); + return wireRep; +} + class tst_WebSocketFrame : public QObject { Q_OBJECT @@ -60,6 +153,15 @@ private Q_SLOTS: void cleanup(); void tst_initialization(); + + void tst_goodFrames_data(); + void tst_goodFrames(); + + void tst_invalidFrames_data(); + void tst_invalidFrames(); + + void tst_malformedFrames_data(); + void tst_malformedFrames(); }; tst_WebSocketFrame::tst_WebSocketFrame() @@ -74,6 +176,8 @@ void tst_WebSocketFrame::cleanupTestCase() void tst_WebSocketFrame::init() { + qRegisterMetaType<QWebSocketProtocol::OpCode>("QWebSocketProtocol::OpCode"); + qRegisterMetaType<QWebSocketProtocol::CloseCode>("QWebSocketProtocol::CloseCode"); } void tst_WebSocketFrame::cleanup() @@ -87,6 +191,358 @@ void tst_WebSocketFrame::tst_initialization() QCOMPARE(frame.payload().length(), 0); } +void tst_WebSocketFrame::tst_goodFrames_data() +{ + QTest::addColumn<int>("rsv1"); + QTest::addColumn<int>("rsv2"); + QTest::addColumn<int>("rsv3"); + QTest::addColumn<quint32>("mask"); + QTest::addColumn<QWebSocketProtocol::OpCode>("opCode"); + QTest::addColumn<bool>("isFinal"); + QTest::addColumn<QByteArray>("payload"); + QTest::addColumn<bool>("isControlFrame"); + QTest::addColumn<bool>("isDataFrame"); + QTest::addColumn<bool>("isContinuationFrame"); + + QTest::newRow("Non masked final text frame with small payload") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_TEXT + << true << QString("Hello world!").toUtf8() + << false << true << false; + QTest::newRow("Non masked final binary frame with small payload") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_BINARY + << true << QByteArray("\x00\x01\x02\x03\x04") + << false << true << false; + QTest::newRow("Non masked final text frame with no payload") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_TEXT + << true << QByteArray() + << false << true << false; + QTest::newRow("Non masked final binary frame with no payload") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_BINARY + << true << QByteArray() + << false << true << false; + + QTest::newRow("Non masked final close frame with small payload") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_CLOSE + << true << QString("Hello world!").toUtf8() + << true << false << false; + QTest::newRow("Non masked final close frame with no payload") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_CLOSE + << true << QByteArray() + << true << false << false; + QTest::newRow("Non masked final ping frame with small payload") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_PING + << true << QString("Hello world!").toUtf8() + << true << false << false; + QTest::newRow("Non masked final pong frame with no payload") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_PONG + << true << QByteArray() + << true << false << false; + + QTest::newRow("Non masked final continuation frame with small payload") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_CONTINUE + << true << QString("Hello world!").toUtf8() + << false << true << true; + QTest::newRow("Non masked non-final continuation frame with small payload") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_CONTINUE + << false << QString("Hello world!").toUtf8() + << false << true << true; +} + +void tst_WebSocketFrame::tst_goodFrames() +{ + QFETCH(int, rsv1); + QFETCH(int, rsv2); + QFETCH(int, rsv3); + QFETCH(quint32, mask); + QFETCH(QWebSocketProtocol::OpCode, opCode); + QFETCH(bool, isFinal); + QFETCH(QByteArray, payload); + QFETCH(bool, isControlFrame); + QFETCH(bool, isDataFrame); + QFETCH(bool, isContinuationFrame); + + FrameHelper helper; + helper.setRsv1(rsv1); + helper.setRsv2(rsv2); + helper.setRsv3(rsv3); + helper.setMask(mask); + helper.setOpCode(opCode); + helper.setFinalFrame(isFinal); + helper.setPayload(payload); + + QByteArray wireRepresentation = helper.wireRepresentation(); + QBuffer buffer; + buffer.setData(wireRepresentation); + buffer.open(QIODevice::ReadOnly); + QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer); + buffer.close(); + QCOMPARE(frame.isValid(), true); + QCOMPARE(frame.rsv1(), rsv1); + QCOMPARE(frame.rsv2(), rsv2); + QCOMPARE(frame.rsv3(), rsv3); + QCOMPARE(frame.hasMask(), (mask != 0)); + QCOMPARE(frame.opCode(), opCode); + QCOMPARE(frame.isFinalFrame(), isFinal); + QCOMPARE(frame.isControlFrame(), isControlFrame); + QCOMPARE(frame.isDataFrame(), isDataFrame); + QCOMPARE(frame.isContinuationFrame(), isContinuationFrame); + QCOMPARE(frame.payload().length(), payload.length()); + QCOMPARE(frame.payload(), payload); +} + +void tst_WebSocketFrame::tst_invalidFrames_data() +{ + QTest::addColumn<int>("rsv1"); + QTest::addColumn<int>("rsv2"); + QTest::addColumn<int>("rsv3"); + QTest::addColumn<quint32>("mask"); + QTest::addColumn<QWebSocketProtocol::OpCode>("opCode"); + QTest::addColumn<bool>("isFinal"); + QTest::addColumn<QByteArray>("payload"); + QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedError"); + + QTest::newRow("RSV1 != 0") + << 1 << 0 << 0 + << 0U << QWebSocketProtocol::OC_TEXT + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("RSV2 != 0") + << 0 << 1 << 0 + << 0U << QWebSocketProtocol::OC_TEXT + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("RSV3 != 0") + << 0 << 0 << 1 + << 0U << QWebSocketProtocol::OC_TEXT + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("RSV1 != 0 and RSV2 != 0") + << 1 << 1 << 0 + << 0U << QWebSocketProtocol::OC_TEXT + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("RSV1 != 0 and RSV3 != 0") + << 1 << 0 << 1 + << 0U << QWebSocketProtocol::OC_TEXT + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("RSV2 != 0 and RSV3 != 0") + << 0 << 1 << 1 + << 0U << QWebSocketProtocol::OC_TEXT + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + + QTest::newRow("Reserved OpCode 3") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_RESERVED_3 + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Reserved OpCode 4") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_RESERVED_4 + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Reserved OpCode 5") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_RESERVED_5 + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Reserved OpCode 6") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_RESERVED_6 + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Reserved OpCode 7") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_RESERVED_7 + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Reserved OpCode B") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_RESERVED_B + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Reserved OpCode C") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_RESERVED_C + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Reserved OpCode D") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_RESERVED_D + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Reserved OpCode E") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_RESERVED_E + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Reserved OpCode F") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_RESERVED_F + << true << QString("Hello world!").toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + + QTest::newRow("Close Frame with payload > 125 bytes") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_CLOSE + << true << QString(126, 'a').toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Non-final Close Frame") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_CLOSE + << false << QString(126, 'a').toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Ping Frame with payload > 125 bytes") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_PING + << true << QString(126, 'a').toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Non-final Ping Frame") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_PING + << false << QString(126, 'a').toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Pong Frame with payload > 125 bytes") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_PONG + << true << QString(126, 'a').toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + QTest::newRow("Non-final Pong Frame") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_PONG + << false << QString(126, 'a').toUtf8() + << QWebSocketProtocol::CC_PROTOCOL_ERROR; +} + +void tst_WebSocketFrame::tst_invalidFrames() +{ + QFETCH(int, rsv1); + QFETCH(int, rsv2); + QFETCH(int, rsv3); + QFETCH(quint32, mask); + QFETCH(QWebSocketProtocol::OpCode, opCode); + QFETCH(bool, isFinal); + QFETCH(QByteArray, payload); + QFETCH(QWebSocketProtocol::CloseCode, expectedError); + + FrameHelper helper; + helper.setRsv1(rsv1); + helper.setRsv2(rsv2); + helper.setRsv3(rsv3); + helper.setMask(mask); + helper.setOpCode(opCode); + helper.setFinalFrame(isFinal); + helper.setPayload(payload); + + QByteArray wireRepresentation = helper.wireRepresentation(); + QBuffer buffer; + buffer.setData(wireRepresentation); + buffer.open(QIODevice::ReadOnly); + QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer); + buffer.close(); + + QVERIFY(!frame.isValid()); + QCOMPARE(frame.closeCode(), expectedError); +} + + +/* + * Incomplete or overly large frames + * Payload must be crafted manually + * + QTest::newRow("Frame Too Big") + << 0 << 0 << 0 + << 0U << QWebSocketProtocol::OC_TEXT + << true << QString(MAX_FRAME_SIZE_IN_BYTES + 1, 'a').toUtf8() + << QWebSocketProtocol::CC_TOO_MUCH_DATA; + + */ +void tst_WebSocketFrame::tst_malformedFrames_data() +{ + QTest::addColumn<QByteArray>("payload"); + QTest::addColumn<QWebSocketProtocol::CloseCode>("expectedError"); + + //too little data + QTest::newRow("No data") << QByteArray() << QWebSocketProtocol::CC_GOING_AWAY; + FrameHelper helper; + helper.setRsv1(0); + helper.setRsv2(0); + helper.setRsv3(0); + helper.setMask(0U); + helper.setOpCode(QWebSocketProtocol::OC_TEXT); + helper.setFinalFrame(true); + helper.setPayload(QString(10, 'a').toUtf8()); + QByteArray wireRep = helper.wireRepresentation(); + + //too little data + //header + payload should be 12 bytes for non-masked payloads < 126 bytes + for (int i = 1; i < 12; ++i) + { + QTest::newRow(QString("Header too small - %1 byte(s)").arg(i).toLatin1().constData()) << wireRep.left(i) << QWebSocketProtocol::CC_GOING_AWAY; + } + //too much data + { + const char bigpayloadIndicator = char(127); + const quint64 payloadSize = MAX_FRAME_SIZE_IN_BYTES + 1; + uchar swapped[8] = {0}; + qToBigEndian<quint64>(payloadSize, swapped); + QTest::newRow("Frame too big") + << wireRep.left(1).append(bigpayloadIndicator).append(reinterpret_cast<char *>(swapped), 8) + << QWebSocketProtocol::CC_TOO_MUCH_DATA; + } + //overlong size field + { + const char largepayloadIndicator = char(126); + const quint16 payloadSize = 120; + uchar swapped[2] = {0}; + qToBigEndian<quint16>(payloadSize, swapped); + QTest::newRow("Overlong 16-bit size field") + << wireRep.left(1).append(largepayloadIndicator).append(reinterpret_cast<char *>(swapped), 2) + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + } + { + const char bigpayloadIndicator = char(127); + quint64 payloadSize = 120; + uchar swapped[8] = {0}; + qToBigEndian<quint64>(payloadSize, swapped); + QTest::newRow("Overlong 64-bit size field; should be 7-bit") + << wireRep.left(1).append(bigpayloadIndicator).append(reinterpret_cast<char *>(swapped), 8) + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + + payloadSize = 256; + qToBigEndian<quint64>(payloadSize, swapped); + QTest::newRow("Overlong 64-bit size field; should be 16-bit") + << wireRep.left(1).append(bigpayloadIndicator).append(reinterpret_cast<char *>(swapped), 8) + << QWebSocketProtocol::CC_PROTOCOL_ERROR; + } +} + +void tst_WebSocketFrame::tst_malformedFrames() +{ + QFETCH(QByteArray, payload); + QFETCH(QWebSocketProtocol::CloseCode, expectedError); + + QBuffer buffer; + buffer.setData(payload); + buffer.open(QIODevice::ReadOnly); + QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer); + buffer.close(); + + QVERIFY(!frame.isValid()); + QCOMPARE(frame.closeCode(), expectedError); +} + QTEST_MAIN(tst_WebSocketFrame) #include "tst_websocketframe.moc" |