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