Пример #1
0
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);
}
Пример #2
0
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);
}
/*!
    \internal
 */
QWebSocketFrame QWebSocketFrame::readFrame(QIODevice *pIoDevice)
{
    bool isDone = false;
    qint64 bytesRead = 0;
    QWebSocketFrame frame;
    quint64 dataWaitSize = 0;
    Q_UNUSED(dataWaitSize); // value is used in MACRO, Q_UNUSED to avoid compiler warnings
    ProcessingState processingState = PS_READ_HEADER;
    ProcessingState returnState = PS_READ_HEADER;
    bool hasMask = false;
    quint64 payloadLength = 0;

    while (!isDone)
    {
        switch (processingState) {
        case PS_WAIT_FOR_MORE_DATA:
            //TODO: waitForReadyRead should really be changed
            //now, when a websocket is used in a GUI thread
            //the GUI will hang for at most 5 seconds
            //maybe, a QStateMachine should be used
            if (!pIoDevice->waitForReadyRead(5000)) {
                frame.setError(QWebSocketProtocol::CC_GOING_AWAY,
                               QObject::tr("Timeout when reading data from socket."));
                processingState = PS_DISPATCH_RESULT;
            } else {
                processingState = returnState;
            }
            break;

        case PS_READ_HEADER:
            if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
                //FIN, RSV1-3, Opcode
                char header[2] = {0};
                bytesRead = pIoDevice->read(header, 2);
                frame.m_isFinalFrame = (header[0] & 0x80) != 0;
                frame.m_rsv1 = (header[0] & 0x40);
                frame.m_rsv2 = (header[0] & 0x20);
                frame.m_rsv3 = (header[0] & 0x10);
                frame.m_opCode = static_cast<QWebSocketProtocol::OpCode>(header[0] & 0x0F);

                //Mask, PayloadLength
                hasMask = (header[1] & 0x80) != 0;
                frame.m_length = (header[1] & 0x7F);

                switch (frame.m_length)
                {
                    case 126:
                    {
                        processingState = PS_READ_PAYLOAD_LENGTH;
                        break;
                    }
                    case 127:
                    {
                        processingState = PS_READ_BIG_PAYLOAD_LENGTH;
                        break;
                    }
                    default:
                    {
                        payloadLength = frame.m_length;
                        processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
                        break;
                    }
                }
                if (!frame.checkValidity())
                    processingState = PS_DISPATCH_RESULT;
            } else {
                WAIT_FOR_MORE_DATA(2);
            }
            break;

        case PS_READ_PAYLOAD_LENGTH:
            if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
                uchar length[2] = {0};
                bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 2);
                if (Q_UNLIKELY(bytesRead == -1)) {
                    frame.setError(QWebSocketProtocol::CC_GOING_AWAY,
                                   QObject::tr("Error occurred while reading from the network: %1")
                                        .arg(pIoDevice->errorString()));
                    processingState = PS_DISPATCH_RESULT;
                } else {
                    payloadLength = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(length));
                    if (Q_UNLIKELY(payloadLength < 126)) {
                        //see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
                        //"in all cases, the minimal number of bytes MUST be used to encode
                        //the length, for example, the length of a 124-byte-long string
                        //can't be encoded as the sequence 126, 0, 124"
                        frame.setError(QWebSocketProtocol::CC_PROTOCOL_ERROR,
                                       QObject::tr("Lengths smaller than 126 " \
                                                   "must be expressed as one byte."));
                        processingState = PS_DISPATCH_RESULT;
                    } else {
                        processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
                    }
                }
            } else {
                WAIT_FOR_MORE_DATA(2);
            }
            break;

        case PS_READ_BIG_PAYLOAD_LENGTH:
            if (Q_LIKELY(pIoDevice->bytesAvailable() >= 8)) {
                uchar length[8] = {0};
                bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 8);
                if (Q_UNLIKELY(bytesRead < 8)) {
                    frame.setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION,
                                   QObject::tr("Something went wrong during "\
                                               "reading from the network."));
                    processingState = PS_DISPATCH_RESULT;
                } else {
                    //Most significant bit must be set to 0 as
                    //per http://tools.ietf.org/html/rfc6455#section-5.2
                    payloadLength = qFromBigEndian<quint64>(length);
                    if (Q_UNLIKELY(payloadLength & (quint64(1) << 63))) {
                        frame.setError(QWebSocketProtocol::CC_PROTOCOL_ERROR,
                                       QObject::tr("Highest bit of payload length is not 0."));
                        processingState = PS_DISPATCH_RESULT;
                    } else if (Q_UNLIKELY(payloadLength <= 0xFFFFu)) {
                        //see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
                        //"in all cases, the minimal number of bytes MUST be used to encode
                        //the length, for example, the length of a 124-byte-long string
                        //can't be encoded as the sequence 126, 0, 124"
                        frame.setError(QWebSocketProtocol::CC_PROTOCOL_ERROR,
                                       QObject::tr("Lengths smaller than 65536 (2^16) " \
                                                   "must be expressed as 2 bytes."));
                        processingState = PS_DISPATCH_RESULT;
                    } else {
                        processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
                    }
                }
            } else {
                WAIT_FOR_MORE_DATA(8);
            }

            break;

        case PS_READ_MASK:
            if (Q_LIKELY(pIoDevice->bytesAvailable() >= 4)) {
                bytesRead = pIoDevice->read(reinterpret_cast<char *>(&frame.m_mask),
                                            sizeof(frame.m_mask));
                if (bytesRead == -1) {
                    frame.setError(QWebSocketProtocol::CC_GOING_AWAY,
                                   QObject::tr("Error while reading from the network: %1.")
                                        .arg(pIoDevice->errorString()));
                    processingState = PS_DISPATCH_RESULT;
                } else {
                    frame.m_mask = qFromBigEndian(frame.m_mask);
                    processingState = PS_READ_PAYLOAD;
                }
            } else {
                WAIT_FOR_MORE_DATA(4);
            }
            break;

        case PS_READ_PAYLOAD:
            if (!payloadLength) {
                processingState = PS_DISPATCH_RESULT;
            } else if (Q_UNLIKELY(payloadLength > MAX_FRAME_SIZE_IN_BYTES)) {
                frame.setError(QWebSocketProtocol::CC_TOO_MUCH_DATA,
                               QObject::tr("Maximum framesize exceeded."));
                processingState = PS_DISPATCH_RESULT;
            } else {
                quint64 bytesAvailable = quint64(pIoDevice->bytesAvailable());
                if (bytesAvailable >= payloadLength) {
                    frame.m_payload = pIoDevice->read(payloadLength);
                    //payloadLength can be safely cast to an integer,
                    //because MAX_FRAME_SIZE_IN_BYTES = MAX_INT
                    if (Q_UNLIKELY(frame.m_payload.length() != int(payloadLength))) {
                        //some error occurred; refer to the Qt documentation of QIODevice::read()
                        frame.setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION,
                                       QObject::tr("Some serious error occurred " \
                                                   "while reading from the network."));
                        processingState = PS_DISPATCH_RESULT;
                    } else {
                        if (hasMask)
                            QWebSocketProtocol::mask(&frame.m_payload, frame.m_mask);
                        processingState = PS_DISPATCH_RESULT;
                    }
                } else {
                    //if payload is too big, then this will timeout
                    WAIT_FOR_MORE_DATA(payloadLength);
                }
            }
            break;

        case PS_DISPATCH_RESULT:
            processingState = PS_READ_HEADER;
            isDone = true;
            break;

        default:
            //should not come here
            qWarning() << "DataProcessor::process: Found invalid state. This should not happen!";
            frame.clear();
            isDone = true;
            break;
        }	//end switch
    }

    return frame;
}
/*!
    \internal
 */
bool QWebSocketDataProcessor::processControlFrame(const QWebSocketFrame &frame)
{
    bool mustStopProcessing = true; //control frames never expect additional frames to be processed
    switch (frame.opCode()) {
    case QWebSocketProtocol::OpCodePing:
        Q_EMIT pingReceived(frame.payload());
        break;

    case QWebSocketProtocol::OpCodePong:
        Q_EMIT pongReceived(frame.payload());
        break;

    case QWebSocketProtocol::OpCodeClose:
    {
        quint16 closeCode = QWebSocketProtocol::CloseCodeNormal;
        QString closeReason;
        QByteArray payload = frame.payload();
        if (Q_UNLIKELY(payload.size() == 1)) {
            //size is either 0 (no close code and no reason)
            //or >= 2 (at least a close code of 2 bytes)
            closeCode = QWebSocketProtocol::CloseCodeProtocolError;
            closeReason = tr("Payload of close frame is too small.");
        } else if (Q_LIKELY(payload.size() > 1)) {
            //close frame can have a close code and reason
            closeCode = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(payload.constData()));
            if (Q_UNLIKELY(!QWebSocketProtocol::isCloseCodeValid(closeCode))) {
                closeCode = QWebSocketProtocol::CloseCodeProtocolError;
                closeReason = tr("Invalid close code %1 detected.").arg(closeCode);
            } else {
                if (payload.size() > 2) {
                    QTextCodec *tc = QTextCodec::codecForName(QByteArrayLiteral("UTF-8"));
                    QTextCodec::ConverterState state(QTextCodec::ConvertInvalidToNull);
                    closeReason = tc->toUnicode(payload.constData() + 2, payload.size() - 2, &state);
                    const bool failed = (state.invalidChars != 0) || (state.remainingChars != 0);
                    if (Q_UNLIKELY(failed)) {
                        closeCode = QWebSocketProtocol::CloseCodeWrongDatatype;
                        closeReason = tr("Invalid UTF-8 code encountered.");
                    }
                }
            }
        }
        Q_EMIT closeReceived(static_cast<QWebSocketProtocol::CloseCode>(closeCode), closeReason);
        break;
    }

    case QWebSocketProtocol::OpCodeContinue:
    case QWebSocketProtocol::OpCodeBinary:
    case QWebSocketProtocol::OpCodeText:
    case QWebSocketProtocol::OpCodeReserved3:
    case QWebSocketProtocol::OpCodeReserved4:
    case QWebSocketProtocol::OpCodeReserved5:
    case QWebSocketProtocol::OpCodeReserved6:
    case QWebSocketProtocol::OpCodeReserved7:
    case QWebSocketProtocol::OpCodeReservedC:
    case QWebSocketProtocol::OpCodeReservedB:
    case QWebSocketProtocol::OpCodeReservedD:
    case QWebSocketProtocol::OpCodeReservedE:
    case QWebSocketProtocol::OpCodeReservedF:
        //do nothing
        //case statements added to make C++ compiler happy
        break;

    default:
        Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeProtocolError,
                                tr("Invalid opcode detected: %1").arg(int(frame.opCode())));
        //do nothing
        break;
    }
    return mustStopProcessing;
}
/*!
    \internal
 */
void QWebSocketDataProcessor::process(QIODevice *pIoDevice)
{
    bool isDone = false;

    while (!isDone) {
        QWebSocketFrame frame = QWebSocketFrame::readFrame(pIoDevice);
        if (Q_LIKELY(frame.isValid())) {
            if (frame.isControlFrame()) {
                isDone = processControlFrame(frame);
            } else {
                //we have a dataframe; opcode can be OC_CONTINUE, OC_TEXT or OC_BINARY
                if (Q_UNLIKELY(!m_isFragmented && frame.isContinuationFrame())) {
                    clear();
                    Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeProtocolError,
                                            tr("Received Continuation frame, while there is " \
                                               "nothing to continue."));
                    return;
                }
                if (Q_UNLIKELY(m_isFragmented && frame.isDataFrame() &&
                               !frame.isContinuationFrame())) {
                    clear();
                    Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeProtocolError,
                                            tr("All data frames after the initial data frame " \
                                               "must have opcode 0 (continuation)."));
                    return;
                }
                if (!frame.isContinuationFrame()) {
                    m_opCode = frame.opCode();
                    m_isFragmented = !frame.isFinalFrame();
                }
                quint64 messageLength = (quint64)(m_opCode == QWebSocketProtocol::OpCodeText)
                        ? m_textMessage.length()
                        : m_binaryMessage.length();
                if (Q_UNLIKELY((messageLength + quint64(frame.payload().length())) >
                               MAX_MESSAGE_SIZE_IN_BYTES)) {
                    clear();
                    Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeTooMuchData,
                                            tr("Received message is too big."));
                    return;
                }

                if (m_opCode == QWebSocketProtocol::OpCodeText) {
                    QString frameTxt = m_pTextCodec->toUnicode(frame.payload().constData(),
                                                               frame.payload().size(),
                                                               m_pConverterState);
                    bool failed = (m_pConverterState->invalidChars != 0)
                            || (frame.isFinalFrame() && (m_pConverterState->remainingChars != 0));
                    if (Q_UNLIKELY(failed)) {
                        clear();
                        Q_EMIT errorEncountered(QWebSocketProtocol::CloseCodeWrongDatatype,
                                                tr("Invalid UTF-8 code encountered."));
                        return;
                    } else {
                        m_textMessage.append(frameTxt);
                        Q_EMIT textFrameReceived(frameTxt, frame.isFinalFrame());
                    }
                } else {
                    m_binaryMessage.append(frame.payload());
                    Q_EMIT binaryFrameReceived(frame.payload(), frame.isFinalFrame());
                }

                if (frame.isFinalFrame()) {
                    if (m_opCode == QWebSocketProtocol::OpCodeText)
                        Q_EMIT textMessageReceived(m_textMessage);
                    else
                        Q_EMIT binaryMessageReceived(m_binaryMessage);
                    clear();
                    isDone = true;
                }
            }
        } else {
            Q_EMIT errorEncountered(frame.closeCode(), frame.closeReason());
            clear();
            isDone = true;
        }
    }
}
Пример #6
0
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();
    QVERIFY(frame.isValid());
    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);
}
Пример #7
0
void tst_WebSocketFrame::tst_copyConstructorAndAssignment()
{
    FrameHelper frameHelper;
    frameHelper.setRsv1(0);
    frameHelper.setRsv2(0);
    frameHelper.setRsv3(0);
    frameHelper.setFinalFrame(true);
    frameHelper.setMask(1234u);
    frameHelper.setOpCode(QWebSocketProtocol::OpCodeBinary);
    frameHelper.setPayload(QByteArrayLiteral("12345"));

    QByteArray payload = frameHelper.wireRepresentation();
    QBuffer buffer(&payload);
    buffer.open(QIODevice::ReadOnly);

    QWebSocketFrame frame = QWebSocketFrame::readFrame(&buffer);
    buffer.close();

    {
        QWebSocketFrame other(frame);
        QCOMPARE(other.closeCode(), frame.closeCode());
        QCOMPARE(other.closeReason(), frame.closeReason());
        QCOMPARE(other.hasMask(), frame.hasMask());
        QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
        QCOMPARE(other.isControlFrame(), frame.isControlFrame());
        QCOMPARE(other.isDataFrame(), frame.isDataFrame());
        QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
        QCOMPARE(other.isValid(), frame.isValid());
        QCOMPARE(other.mask(), frame.mask());
        QCOMPARE(other.opCode(), frame.opCode());
        QCOMPARE(other.payload(), frame.payload());
        QCOMPARE(other.rsv1(), frame.rsv1());
        QCOMPARE(other.rsv2(), frame.rsv2());
        QCOMPARE(other.rsv3(), frame.rsv3());
    }
    {
        QWebSocketFrame other;
        other = frame;
        QCOMPARE(other.closeCode(), frame.closeCode());
        QCOMPARE(other.closeReason(), frame.closeReason());
        QCOMPARE(other.hasMask(), frame.hasMask());
        QCOMPARE(other.isContinuationFrame(), frame.isContinuationFrame());
        QCOMPARE(other.isControlFrame(), frame.isControlFrame());
        QCOMPARE(other.isDataFrame(), frame.isDataFrame());
        QCOMPARE(other.isFinalFrame(), frame.isFinalFrame());
        QCOMPARE(other.isValid(), frame.isValid());
        QCOMPARE(other.mask(), frame.mask());
        QCOMPARE(other.opCode(), frame.opCode());
        QCOMPARE(other.payload(), frame.payload());
        QCOMPARE(other.rsv1(), frame.rsv1());
        QCOMPARE(other.rsv2(), frame.rsv2());
        QCOMPARE(other.rsv3(), frame.rsv3());
    }
}
Пример #8
0
void tst_WebSocketFrame::tst_initialization()
{
    QWebSocketFrame frame;
    QVERIFY(!frame.isValid());
    QCOMPARE(frame.payload().length(), 0);
}