/*!
    \internal
 */
void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
{
    if (Q_UNLIKELY(!m_pSocket))
        return;
    if (!m_isClosingHandshakeSent) {
        Q_Q(QWebSocket);
        const quint16 code = qToBigEndian<quint16>(closeCode);
        QByteArray payload;
        payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
        if (!reason.isEmpty())
            payload.append(reason.toUtf8());
        quint32 maskingKey = 0;
        if (m_mustMask) {
            maskingKey = generateMaskingKey();
            QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
        }
        QByteArray frame = getFrameHeader(QWebSocketProtocol::OpCodeClose,
                                          payload.size(), maskingKey, true);
        frame.append(payload);
        m_pSocket->write(frame);
        m_pSocket->flush();

        m_isClosingHandshakeSent = true;

        Q_EMIT q->aboutToClose();
    }
    m_pSocket->close();
}
Exemple #2
0
/*!
 * \brief Gracefully closes the socket with the given \a closeCode and \a reason. Any data in the write buffer is flushed before the socket is closed.
 * \param closeCode The WebSocketProtocol::CloseCode indicating the reason to close.
 * \param reason A string describing the error more in detail
 */
void WebSocket::close(WebSocketProtocol::CloseCode closeCode, QString reason)
{
	if (!m_isClosingHandshakeSent)
	{
		quint32 maskingKey = 0;
		if (m_mustMask)
		{
			maskingKey = generateMaskingKey();
		}
		quint16 code = qToBigEndian<quint16>(closeCode);
		QByteArray payload;
		payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
		if (!reason.isEmpty())
		{
			payload.append(reason.toUtf8());
		}
		if (m_mustMask)
		{
			WebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
		}
		QByteArray frame = getFrameHeader(WebSocketProtocol::OC_CLOSE, payload.size(), maskingKey, true);
		frame.append(payload);
		m_pSocket->write(frame);
		m_pSocket->flush();

		m_isClosingHandshakeSent = true;

		Q_EMIT aboutToClose();
	}
	m_pSocket->close();
}
Exemple #3
0
struct wsFrame* createWSFrame(char *data, int len, unsigned char opcode){
	struct wsFrame* frame = (struct wsFrame*)malloc(sizeof(struct wsFrame));
	frame->fin = 1;
	frame->opcode = opcode;
	frame->mask = 1;
	frame->len = len;
	frame->maskingKey = generateMaskingKey();
	frame->data = (char*)malloc(frame->len+1);
	strcpy(frame->data, data);
	return frame;
}
/*!
 \internal
 */
void QWebSocketPrivate::processPing(const QByteArray &data)
{
    Q_ASSERT(m_pSocket);
    quint32 maskingKey = 0;
    if (m_mustMask)
        maskingKey = generateMaskingKey();
    m_pSocket->write(getFrameHeader(QWebSocketProtocol::OpCodePong, data.size(), maskingKey, true));
    if (data.size() > 0) {
        QByteArray maskedData = data;
        if (m_mustMask)
            QWebSocketProtocol::mask(&maskedData, maskingKey);
        m_pSocket->write(maskedData);
    }
}
/*!
    \internal
 */
void QWebSocketPrivate::ping(const QByteArray &payload)
{
    QByteArray payloadTruncated = payload.left(125);
    m_pingTimer.restart();
    quint32 maskingKey = 0;
    if (m_mustMask)
        maskingKey = generateMaskingKey();
    QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OpCodePing, payloadTruncated.size(),
                                          maskingKey, true);
    if (m_mustMask)
        QWebSocketProtocol::mask(&payloadTruncated, maskingKey);
    pingFrame.append(payloadTruncated);
    qint64 ret = writeFrame(pingFrame);
    Q_UNUSED(ret);
}
void EnginioBackendConnection::ping()
{
    if (_sentCloseFrame)
        return;

    // The WebSocket server should accept ping frames without payload according to
    // the specification, but ours does not, so let's add a dummy payload.
    QByteArray dummy;
    dummy.append(QStringLiteral("Ping.").toUtf8());
    QByteArray maskingKey = generateMaskingKey();
    QByteArray message = constructFrameHeader(/*isFinalFragment*/ true, PingOp, dummy.size(), maskingKey);
    Q_ASSERT(!message.isEmpty());

    maskData(dummy, maskingKey);
    message.append(dummy);
    _tcpSocket->write(message);
}
void EnginioBackendConnection::close(WebSocketCloseStatus closeStatus)
{
    if (_sentCloseFrame)
        return;

    _sentCloseFrame = true;
    _keepAliveTimer.stop();

    QByteArray payload;
    quint16 closeStatusBigEndian = qToBigEndian<quint16>(closeStatus);
    payload.append(reinterpret_cast<char*>(&closeStatusBigEndian), DefaultHeaderLength);

    QByteArray maskingKey = generateMaskingKey();
    QByteArray message = constructFrameHeader(/*isFinalFragment*/ true, ConnectionCloseOp, payload.size(), maskingKey);
    Q_ASSERT(!message.isEmpty());

    maskData(payload, maskingKey);
    message.append(payload);
    _tcpSocket->write(message);
}
/*!
 * \internal
 */
qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
{
    qint64 payloadWritten = 0;
    if (Q_UNLIKELY(!m_pSocket) || (state() != QAbstractSocket::ConnectedState))
        return payloadWritten;

    Q_Q(QWebSocket);
    const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
                QWebSocketProtocol::OpCodeBinary : QWebSocketProtocol::OpCodeText;

    int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
    QByteArray tmpData(data);
    tmpData.detach();
    char *payload = tmpData.data();
    quint64 sizeLeft = quint64(data.size()) % FRAME_SIZE_IN_BYTES;
    if (Q_LIKELY(sizeLeft))
        ++numFrames;

    //catch the case where the payload is zero bytes;
    //in this case, we still need to send a frame
    if (Q_UNLIKELY(numFrames == 0))
        numFrames = 1;
    quint64 currentPosition = 0;
    qint64 bytesWritten = 0;
    quint64 bytesLeft = data.size();

    for (int i = 0; i < numFrames; ++i) {
        quint32 maskingKey = 0;
        if (m_mustMask)
            maskingKey = generateMaskingKey();

        const bool isLastFrame = (i == (numFrames - 1));
        const bool isFirstFrame = (i == 0);

        const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
        const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode
                                                               : QWebSocketProtocol::OpCodeContinue;

        //write header
        bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));

        //write payload
        if (Q_LIKELY(size > 0)) {
            char *currentData = payload + currentPosition;
            if (m_mustMask)
                QWebSocketProtocol::mask(currentData, size, maskingKey);
            qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
            if (Q_LIKELY(written > 0)) {
                bytesWritten += written;
                payloadWritten += written;
            } else {
                m_pSocket->flush();
                setErrorString(QWebSocket::tr("Error writing bytes to socket: %1.")
                               .arg(m_pSocket->errorString()));
                Q_EMIT q->error(QAbstractSocket::NetworkError);
                break;
            }
        }
        currentPosition += size;
        bytesLeft -= size;
    }
    if (Q_UNLIKELY(payloadWritten != data.size())) {
        setErrorString(QWebSocket::tr("Bytes written %1 != %2.")
                       .arg(payloadWritten).arg(data.size()));
        Q_EMIT q->error(QAbstractSocket::NetworkError);
    }
    return payloadWritten;
}
void EnginioBackendConnection::onSocketReadyRead()
{
    //     WebSocket Protocol (RFC6455)
    //     Base Framing Protocol
    //     http://tools.ietf.org/html/rfc6455#section-5.2
    //
    //      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    //     +-+-+-+-+-------+-+-------------+-------------------------------+
    //     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
    //     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
    //     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
    //     | |1|2|3|       |K|             |                               |
    //     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
    //     |     Extended payload length continued, if payload len == 127  |
    //     + - - - - - - - - - - - - - - - +-------------------------------+
    //     |                               |Masking-key, if MASK set to 1  |
    //     +-------------------------------+-------------------------------+
    //     | Masking-key (continued)       |          Payload Data         |
    //     +-------------------------------- - - - - - - - - - - - - - - - +
    //     :                     Payload Data continued ...                :
    //     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    //     |                     Payload Data continued ...                |
    //     +---------------------------------------------------------------+

    while (_tcpSocket->bytesAvailable()) {
        switch (_protocolDecodeState) {
        case HandshakePending: {
            // The response is closed by a CRLF line on its own (e.g. ends with two newlines).
            while (_handshakeReply.isEmpty()
                   || (!_handshakeReply.endsWith(QString(CRLF % CRLF).toUtf8())
                   // According to documentation QIODevice::readLine replaces newline characters on
                   // Windows with '\n', so just to be on the safe side:
                   && !_handshakeReply.endsWith(QByteArrayLiteral("\n\n")))) {

                if (!_tcpSocket->bytesAvailable())
                    return;

                _handshakeReply.append(_tcpSocket->readLine());
            }

            QString response = QString::fromUtf8(_handshakeReply);
            _handshakeReply.clear();

            int statusCode = extractResponseStatus(response);
            QString secWebSocketAccept = extractResponseHeader(SecWebSocketAcceptHeader, response, /* ignoreCase */ false);
            bool hasValidKey = secWebSocketAccept == gBase64EncodedSha1VerificationKey;

            if (statusCode != 101 || !hasValidKey
                    || extractResponseHeader(UpgradeHeader, response) != QStringLiteral("websocket")
                    || extractResponseHeader(ConnectionHeader, response) != QStringLiteral("upgrade")
                    )
                return protocolError("Handshake failed!");

            _keepAliveTimer.start(TwoMinutes, this);
            _protocolDecodeState = FrameHeaderPending;
            emit stateChanged(ConnectedState);
        } // Fall-through.

        case FrameHeaderPending: {
            if (quint64(_tcpSocket->bytesAvailable()) < DefaultHeaderLength)
                return;

            // Large payload.
            if (_payloadLength == LargePayloadMarker) {
                if (quint64(_tcpSocket->bytesAvailable()) < LargePayloadHeaderLength)
                    return;

                char data[LargePayloadHeaderLength];
                if (quint64(_tcpSocket->read(data, LargePayloadHeaderLength)) != LargePayloadHeaderLength)
                    return protocolError("Reading large payload length failed!");

                if (data[0] & MSB)
                    return protocolError("The most significant bit of a large payload length must be 0!", MessageTooBigCloseStatus);

                // 8 bytes interpreted as a 64-bit unsigned integer
                _payloadLength = qFromBigEndian<quint64>(reinterpret_cast<uchar*>(data));
                _protocolDecodeState = PayloadDataPending;

                break;
            }

            char data[DefaultHeaderLength];
            if (quint64(_tcpSocket->read(data, DefaultHeaderLength)) != DefaultHeaderLength)
                return protocolError("Reading header failed!");

            if (!_payloadLength) {
                // This is the initial frame header data.
                _isFinalFragment = (data[0] & FIN);
                _protocolOpcode = static_cast<WebSocketOpcode>(data[0] & OPC);
                _isPayloadMasked = (data[1] & MSK);
                _payloadLength = (data[1] & LEN);

                if (_isPayloadMasked)
                    return protocolError("Invalid masked frame received from server.");

                // For data length 0-125 LEN is the payload length.
                if (_payloadLength < NormalPayloadMarker)
                    _protocolDecodeState = PayloadDataPending;

            } else {
                Q_ASSERT(_payloadLength == NormalPayloadMarker);
                // Normal sized payload: 2 bytes interpreted as the payload
                // length expressed in network byte order (e.g. big endian).
                _payloadLength = qFromBigEndian<quint16>(reinterpret_cast<uchar*>(data));
                _protocolDecodeState = PayloadDataPending;
            }

            break;
        }

        case PayloadDataPending: {
            if (static_cast<quint64>(_tcpSocket->bytesAvailable()) < _payloadLength)
                return;

            if (_protocolOpcode == ConnectionCloseOp) {
                WebSocketCloseStatus closeStatus = UnknownCloseStatus;
                if (_payloadLength >= DefaultHeaderLength) {
                    char data[DefaultHeaderLength];
                    if (quint64(_tcpSocket->read(data, DefaultHeaderLength)) != DefaultHeaderLength)
                        return protocolError("Reading connection close status failed!");

                     closeStatus = static_cast<WebSocketCloseStatus>(qFromBigEndian<quint16>(reinterpret_cast<uchar*>(data)));

                     // The body may contain UTF-8-encoded data with value /reason/,
                     // the interpretation of this data is however not defined by the
                     // specification. Further more the data is not guaranteed to be
                     // human readable, thus it is safe for us to just discard the rest
                     // of the message at this point.
                }

                qDebug() << "Connection closed by the server with status:" << closeStatus;

                QJsonObject data;
                data[EnginioString::messageType] = QStringLiteral("close");
                data[EnginioString::status] = closeStatus;
                emit dataReceived(data);

                close(closeStatus);

                _tcpSocket->close();
                return;
            }

            // We received data from the server so restart the timer.
            _keepAliveTimer.start(TwoMinutes, this);

            _applicationData.append(_tcpSocket->read(_payloadLength));
            _protocolDecodeState = FrameHeaderPending;
            _payloadLength = 0;

            if (!_isFinalFragment)
                break;

            switch (_protocolOpcode) {
            case TextFrameOp: {
                QJsonObject data = QJsonDocument::fromJson(_applicationData).object();
                data[EnginioString::messageType] = QStringLiteral("data");
                emit dataReceived(data);
                break;
            }
            case PingOp:{
                // We must send back identical application data as found in the message.
                QByteArray payload = _applicationData;
                QByteArray maskingKey = generateMaskingKey();
                QByteArray message = constructFrameHeader(/*isFinalFragment*/ true, PongOp, payload.size(), maskingKey);
                Q_ASSERT(!message.isEmpty());
                maskData(payload, maskingKey);
                message.append(payload);
                _tcpSocket->write(message);
                break;
            }
            case PongOp:
                _pingTimeoutTimer.stop();
                emit pong();
                break;
            default:
                protocolError("WebSocketOpcode not yet supported.", UnsupportedDataTypeCloseStatus);
                qWarning() << "\t\t->" << _protocolOpcode;
            }

            _applicationData.clear();

            break;
        }
        }
    }
}
Exemple #10
0
/*!
	\internal
 */
void WebSocket::processControlFrame(WebSocketProtocol::OpCode opCode, QByteArray frame)
{
	switch (opCode)
	{
		case WebSocketProtocol::OC_PING:
		{
			quint32 maskingKey = 0;
			if (m_mustMask)
			{
				maskingKey = generateMaskingKey();
			}
			m_pSocket->write(getFrameHeader(WebSocketProtocol::OC_PONG, frame.size(), maskingKey, true));
			if (frame.size() > 0)
			{
				if (m_mustMask)
				{
					WebSocketProtocol::mask(&frame, maskingKey);
				}
				m_pSocket->write(frame);
			}
			break;
		}
		case WebSocketProtocol::OC_PONG:
		{
			Q_EMIT pong(static_cast<quint64>(m_pingTimer.elapsed()));
			break;
		}
		case WebSocketProtocol::OC_CLOSE:
		{
			quint16 closeCode = WebSocketProtocol::CC_NORMAL;
			QString closeReason;
			if (frame.size() > 0)   //close frame can have a close code and reason
			{
				closeCode = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(frame.constData()));
				if (!WebSocketProtocol::isCloseCodeValid(closeCode))
				{
					closeCode = WebSocketProtocol::CC_PROTOCOL_ERROR;
					closeReason = QString("Invalid close code %1 detected").arg(closeCode);
				}
				else
				{
					if (frame.size() > 2)
					{
						QTextCodec *tc = QTextCodec::codecForName("UTF-8");
						QTextCodec::ConverterState state(QTextCodec::ConvertInvalidToNull);
						closeReason = tc->toUnicode(frame.constData() + 2, frame.size() - 2, &state);
						bool failed = (state.invalidChars != 0) || (state.remainingChars != 0);
						if (failed)
						{
							closeCode = WebSocketProtocol::CC_WRONG_DATATYPE;
							closeReason = "Invalid UTF-8 code encountered.";
						}
					}
				}
			}
			m_isClosingHandshakeReceived = true;
			close(static_cast<WebSocketProtocol::CloseCode>(closeCode), closeReason);
			break;
		}
		case WebSocketProtocol::OC_CONTINUE:
		case WebSocketProtocol::OC_BINARY:
		case WebSocketProtocol::OC_TEXT:
		case WebSocketProtocol::OC_RESERVED_3:
		case WebSocketProtocol::OC_RESERVED_4:
		case WebSocketProtocol::OC_RESERVED_5:
		case WebSocketProtocol::OC_RESERVED_6:
		case WebSocketProtocol::OC_RESERVED_7:
		case WebSocketProtocol::OC_RESERVED_B:
		case WebSocketProtocol::OC_RESERVED_D:
		case WebSocketProtocol::OC_RESERVED_E:
		case WebSocketProtocol::OC_RESERVED_F:
		case WebSocketProtocol::OC_RESERVED_V:
		{
			//do nothing
			//case added to make C++ compiler happy
			break;
		}
		default:
		{
			qDebug() << "WebSocket::processData: Invalid opcode detected:" << static_cast<int>(opCode);
			//Do nothing
			break;
		}
	}
}
Exemple #11
0
/*!
 * \internal
 */
qint64 WebSocket::doWriteFrames(const QByteArray &data, bool isBinary)
{
	const WebSocketProtocol::OpCode firstOpCode = isBinary ? WebSocketProtocol::OC_BINARY : WebSocketProtocol::OC_TEXT;

	int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
	QByteArray tmpData(data);
	tmpData.detach();
	char *payload = tmpData.data();
	quint64 sizeLeft = static_cast<quint64>(data.size()) % FRAME_SIZE_IN_BYTES;
	if (sizeLeft)
	{
		++numFrames;
	}
	if (numFrames == 0)     //catch the case where the payload is zero bytes; in that case, we still need to send a frame
	{
		numFrames = 1;
	}
	quint64 currentPosition = 0;
	qint64 bytesWritten = 0;
	qint64 payloadWritten = 0;
	quint64 bytesLeft = data.size();

	for (int i = 0; i < numFrames; ++i)
	{
		quint32 maskingKey = 0;
		if (m_mustMask)
		{
			maskingKey = generateMaskingKey();
		}

		bool isLastFrame = (i == (numFrames - 1));
		bool isFirstFrame = (i == 0);

		quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
		WebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode : WebSocketProtocol::OC_CONTINUE;

		//write header
		bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));

		//write payload
		if (size > 0)
		{
			char *currentData = payload + currentPosition;
			if (m_mustMask)
			{
				WebSocketProtocol::mask(currentData, size, maskingKey);
			}
			qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
			if (written > 0)
			{
				bytesWritten += written;
				payloadWritten += written;
			}
			else
			{
				setErrorString("WebSocket::doWriteFrames: Error writing bytes to socket: " + m_pSocket->errorString());
				qDebug() << errorString();
				m_pSocket->flush();
				Q_EMIT error(QAbstractSocket::NetworkError);
				break;
			}
		}
		currentPosition += size;
		bytesLeft -= size;
	}
	if (payloadWritten != data.size())
	{
		setErrorString("Bytes written " + QString::number(payloadWritten) + " != " + QString::number(data.size()));
		qDebug() << errorString();
		Q_EMIT error(QAbstractSocket::NetworkError);
	}
	return payloadWritten;
}