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()); } }
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); }
/*! \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; } } }