Example #1
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();
}
/*!
    \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();
}
/*!
    \internal
 */
void QWebSocketPrivate::ping(const QByteArray &payload)
{
    QByteArray payloadTruncated = payload.left(125);
    m_pingTimer.restart();
    QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OpCodePing, payloadTruncated.size(),
                                          0 /*do not mask*/, true);
    pingFrame.append(payloadTruncated);
    qint64 ret = writeFrame(pingFrame);
    Q_UNUSED(ret);
}
/*!
 \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);
}
/*!
 * \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;
}
Example #7
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;
		}
	}
}
Example #8
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;
}
Example #9
0
/*!
 * \brief Pings the server to indicate that the connection is still alive.
 *
 * \sa pong()
 */
void WebSocket::ping()
{
	m_pingTimer.restart();
	QByteArray pingFrame = getFrameHeader(WebSocketProtocol::OC_PING, 0, 0, true);
	writeFrame(pingFrame);
}