From ea9ec768a88dad43ec9dc41556daf412ac41305e Mon Sep 17 00:00:00 2001 From: Kurt Pattyn Date: Sat, 24 Aug 2013 23:51:30 +0200 Subject: Added d-pointer paradigm to QWebSocket implementation --- source/qwebsocket.cpp | 953 ++-------------------------------- source/qwebsocket.h | 70 +-- source/qwebsocket_p.cpp | 1147 +++++++++++++++++++++++++++++++++++++++++ source/qwebsocket_p.h | 165 ++++++ source/qwebsocketserver_p.cpp | 3 +- source/websocket.pri | 8 +- 6 files changed, 1368 insertions(+), 978 deletions(-) create mode 100644 source/qwebsocket_p.cpp create mode 100644 source/qwebsocket_p.h diff --git a/source/qwebsocket.cpp b/source/qwebsocket.cpp index 8ac59c3..b2e7d22 100644 --- a/source/qwebsocket.cpp +++ b/source/qwebsocket.cpp @@ -1,15 +1,9 @@ #include "qwebsocket.h" -#include "handshakerequest_p.h" -#include "handshakeresponse_p.h" +#include "qwebsocket_p.h" #include #include #include -#include -#include -#include -#include #include -#include #include @@ -169,54 +163,8 @@ const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //maximum size of a frame whe */ QWebSocket::QWebSocket(QString origin, QWebSocketProtocol::Version version, QObject *parent) : QObject(parent), - m_pSocket(new QTcpSocket(this)), - m_errorString(), - m_version(version), - m_resourceName(), - m_requestUrl(), - m_origin(origin), - m_protocol(""), - m_extension(""), - m_socketState(QAbstractSocket::UnconnectedState), - m_key(), - m_mustMask(true), - m_isClosingHandshakeSent(false), - m_isClosingHandshakeReceived(false), - m_pingTimer(), - m_dataProcessor() + d_ptr(new QWebSocketPrivate(origin, version, this, this)) { - makeConnections(m_pSocket); - qsrand(static_cast(QDateTime::currentMSecsSinceEpoch())); -} - -//only called by upgradeFrom -/*! - \internal - Constructor used for the server implementation. Should never be called directly. - - pTcpSocket The tcp socket to use for this websocket - version The version of the protocol to speak (currently only V13 is supported) - parent The parent object of the QWebSocket object -*/ -QWebSocket::QWebSocket(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version, QObject *parent) : - QObject(parent), - m_pSocket(pTcpSocket), - m_errorString(pTcpSocket->errorString()), - m_version(version), - m_resourceName(), - m_requestUrl(), - m_origin(), - m_protocol(), - m_extension(), - m_socketState(pTcpSocket->state()), - m_key(), - m_mustMask(true), - m_isClosingHandshakeSent(false), - m_isClosingHandshakeReceived(false), - m_pingTimer(), - m_dataProcessor() -{ - makeConnections(m_pSocket); } /*! @@ -224,13 +172,8 @@ QWebSocket::QWebSocket(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version versi */ QWebSocket::~QWebSocket() { - if (state() == QAbstractSocket::ConnectedState) - { - close(QWebSocketProtocol::CC_GOING_AWAY, "Connection closed"); - } - releaseConnections(m_pSocket); - m_pSocket->deleteLater(); - m_pSocket = 0; + delete d_ptr; + //d_ptr = 0; } /*! @@ -238,7 +181,7 @@ QWebSocket::~QWebSocket() */ void QWebSocket::abort() { - m_pSocket->abort(); + d_ptr->abort(); } /*! @@ -247,7 +190,17 @@ void QWebSocket::abort() */ QAbstractSocket::SocketError QWebSocket::error() const { - return m_pSocket->error(); + return d_ptr->error(); +} + +//only called by QWebSocketPrivate::upgradeFrom +/*! + \internal + */ +QWebSocket::QWebSocket(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version, QObject *parent) : + QObject(parent), + d_ptr(new QWebSocketPrivate(pTcpSocket, version, this, this)) +{ } /*! @@ -257,14 +210,7 @@ QAbstractSocket::SocketError QWebSocket::error() const */ QString QWebSocket::errorString() const { - if (!m_errorString.isEmpty()) - { - return m_errorString; - } - else - { - return m_pSocket->errorString(); - } + return d_ptr->errorString(); } /*! @@ -279,7 +225,7 @@ QString QWebSocket::errorString() const */ bool QWebSocket::flush() { - return m_pSocket->flush(); + return d_ptr->flush(); } /*! @@ -290,7 +236,7 @@ bool QWebSocket::flush() */ qint64 QWebSocket::send(const char *message) { - return send(QString::fromUtf8(message)); + return d_ptr->send(message); } /** @@ -300,7 +246,7 @@ qint64 QWebSocket::send(const char *message) */ qint64 QWebSocket::send(const QString &message) { - return doWriteData(message.toUtf8(), false); + return d_ptr->send(message); } /** @@ -310,26 +256,7 @@ qint64 QWebSocket::send(const QString &message) */ qint64 QWebSocket::send(const QByteArray &data) { - return doWriteData(data, true); -} - -/*! - \internal - */ -QWebSocket *QWebSocket::upgradeFrom(QTcpSocket *pTcpSocket, - const HandshakeRequest &request, - const HandshakeResponse &response, - QObject *parent) -{ - QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.getAcceptedVersion(), parent); - pWebSocket->setExtension(response.getAcceptedExtension()); - pWebSocket->setOrigin(request.getOrigin()); - pWebSocket->setRequestUrl(request.getRequestUrl()); - pWebSocket->setProtocol(response.getAcceptedProtocol()); - pWebSocket->setResourceName(request.getRequestUrl().toString(QUrl::RemoveUserInfo)); - pWebSocket->enableMasking(false); //a server should not send masked frames - - return pWebSocket; + return d_ptr->send(data); } /*! @@ -339,34 +266,7 @@ QWebSocket *QWebSocket::upgradeFrom(QTcpSocket *pTcpSocket, */ void QWebSocket::close(QWebSocketProtocol::CloseCode closeCode, QString reason) { - if (!m_isClosingHandshakeSent) - { - quint32 maskingKey = 0; - if (m_mustMask) - { - maskingKey = generateMaskingKey(); - } - quint16 code = qToBigEndian(closeCode); - QByteArray payload; - payload.append(static_cast(static_cast(&code)), 2); - if (!reason.isEmpty()) - { - payload.append(reason.toUtf8()); - } - if (m_mustMask) - { - QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey); - } - QByteArray frame = getFrameHeader(QWebSocketProtocol::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(); + d_ptr->close(closeCode, reason); } /*! @@ -378,22 +278,7 @@ void QWebSocket::close(QWebSocketProtocol::CloseCode closeCode, QString reason) */ void QWebSocket::open(const QUrl &url, bool mask) { - m_dataProcessor.clear(); - m_isClosingHandshakeReceived = false; - m_isClosingHandshakeSent = false; - - setRequestUrl(url); - QString resourceName = url.path() + url.query(); - if (resourceName.isEmpty()) - { - resourceName = "/"; - } - setResourceName(resourceName); - enableMasking(mask); - - setSocketState(QAbstractSocket::ConnectingState); - - m_pSocket->connectToHost(url.host(), url.port(80)); + d_ptr->open(url, mask); } /*! @@ -403,125 +288,7 @@ void QWebSocket::open(const QUrl &url, bool mask) */ void QWebSocket::ping() { - m_pingTimer.restart(); - QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OC_PING, 0, 0, true); - writeFrame(pingFrame); -} - -/*! - \internal - Sets the version to use for the websocket protocol; this must be set before the socket is opened. -*/ -void QWebSocket::setVersion(QWebSocketProtocol::Version version) -{ - m_version = version; -} - -/*! - \internal - Sets the resource name of the connection; must be set before the socket is openend -*/ -void QWebSocket::setResourceName(QString resourceName) -{ - m_resourceName = resourceName; -} - -/*! - \internal - */ -void QWebSocket::setRequestUrl(QUrl requestUrl) -{ - m_requestUrl = requestUrl; -} - -/*! - \internal - */ -void QWebSocket::setOrigin(QString origin) -{ - m_origin = origin; -} - -/*! - \internal - */ -void QWebSocket::setProtocol(QString protocol) -{ - m_protocol = protocol; -} - -/*! - \internal - */ -void QWebSocket::setExtension(QString extension) -{ - m_extension = extension; -} - -/*! - \internal - */ -void QWebSocket::enableMasking(bool enable) -{ - m_mustMask = enable; -} - -/*! - * \internal - */ -qint64 QWebSocket::doWriteData(const QByteArray &data, bool isBinary) -{ - return doWriteFrames(data, isBinary); -} - -/*! - * \internal - */ -void QWebSocket::makeConnections(const QTcpSocket *pTcpSocket) -{ - //pass through signals - connect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(error(QAbstractSocket::SocketError))); - connect(pTcpSocket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *))); - connect(pTcpSocket, SIGNAL(readChannelFinished()), this, SIGNAL(readChannelFinished())); - //connect(pTcpSocket, SIGNAL(aboutToClose()), this, SIGNAL(aboutToClose())); - //connect(pTcpSocket, SIGNAL(bytesWritten(qint64)), this, SIGNAL(bytesWritten(qint64))); - - //catch signals - connect(pTcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(processStateChanged(QAbstractSocket::SocketState))); - connect(pTcpSocket, SIGNAL(readyRead()), this, SLOT(processData())); - - connect(&m_dataProcessor, SIGNAL(controlFrameReceived(QWebSocketProtocol::OpCode, QByteArray)), this, SLOT(processControlFrame(QWebSocketProtocol::OpCode, QByteArray))); - connect(&m_dataProcessor, SIGNAL(textFrameReceived(QString,bool)), this, SIGNAL(textFrameReceived(QString,bool))); - connect(&m_dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)), this, SIGNAL(binaryFrameReceived(QByteArray,bool))); - connect(&m_dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)), this, SIGNAL(binaryMessageReceived(QByteArray))); - connect(&m_dataProcessor, SIGNAL(textMessageReceived(QString)), this, SIGNAL(textMessageReceived(QString))); - connect(&m_dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)), this, SLOT(close(QWebSocketProtocol::CloseCode,QString))); -} - -/*! - * \internal - */ -void QWebSocket::releaseConnections(const QTcpSocket *pTcpSocket) -{ - if (pTcpSocket) - { - //pass through signals - disconnect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(error(QAbstractSocket::SocketError))); - disconnect(pTcpSocket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *))); - disconnect(pTcpSocket, SIGNAL(readChannelFinished()), this, SIGNAL(readChannelFinished())); - //disconnect(pTcpSocket, SIGNAL(aboutToClose()), this, SIGNAL(aboutToClose())); - //disconnect(pTcpSocket, SIGNAL(bytesWritten(qint64)), this, SIGNAL(bytesWritten(qint64))); - - //catched signals - disconnect(pTcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(processStateChanged(QAbstractSocket::SocketState))); - disconnect(pTcpSocket, SIGNAL(readyRead()), this, SLOT(processData())); - } - disconnect(&m_dataProcessor, SIGNAL(controlFrameReceived(QWebSocketProtocol::OpCode,QByteArray)), this, SLOT(processControlFrame(QWebSocketProtocol::OpCode,QByteArray))); - disconnect(&m_dataProcessor, SIGNAL(textFrameReceived(QString,bool)), this, SIGNAL(textFrameReceived(QString,bool))); - disconnect(&m_dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)), this, SIGNAL(binaryFrameReceived(QByteArray,bool))); - disconnect(&m_dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)), this, SIGNAL(binaryMessageReceived(QByteArray))); - disconnect(&m_dataProcessor, SIGNAL(textMessageReceived(QString)), this, SIGNAL(textMessageReceived(QString))); - disconnect(&m_dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)), this, SLOT(close(QWebSocketProtocol::CloseCode,QString))); + d_ptr->ping(); } /*! @@ -529,7 +296,7 @@ void QWebSocket::releaseConnections(const QTcpSocket *pTcpSocket) */ QWebSocketProtocol::Version QWebSocket::version() { - return m_version; + return d_ptr->version(); } /** @@ -537,7 +304,7 @@ QWebSocketProtocol::Version QWebSocket::version() */ QString QWebSocket::resourceName() { - return m_resourceName; + return d_ptr->resourceName(); } /*! @@ -545,7 +312,7 @@ QString QWebSocket::resourceName() */ QUrl QWebSocket::requestUrl() { - return m_requestUrl; + return d_ptr->requestUrl(); } /*! @@ -553,7 +320,7 @@ QUrl QWebSocket::requestUrl() */ QString QWebSocket::origin() { - return m_origin; + return d_ptr->origin(); } /*! @@ -561,7 +328,7 @@ QString QWebSocket::origin() */ QString QWebSocket::protocol() { - return m_protocol; + return d_ptr->protocol(); } /*! @@ -569,550 +336,7 @@ QString QWebSocket::protocol() */ QString QWebSocket::extension() { - return m_extension; -} - -/*! - * \internal - */ -QByteArray QWebSocket::getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength, quint32 maskingKey, bool lastFrame) const -{ - QByteArray header; - quint8 byte = 0x00; - bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL; - - if (ok) - { - //FIN, RSV1-3, opcode - byte = static_cast((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00)); //FIN, opcode - //RSV-1, RSV-2 and RSV-3 are zero - header.append(static_cast(byte)); - - //Now write the masking bit and the payload length byte - byte = 0x00; - if (maskingKey != 0) - { - byte |= 0x80; - } - if (payloadLength <= 125) - { - byte |= static_cast(payloadLength); - header.append(static_cast(byte)); - } - else if (payloadLength <= 0xFFFFU) - { - byte |= 126; - header.append(static_cast(byte)); - quint16 swapped = qToBigEndian(static_cast(payloadLength)); - header.append(static_cast(static_cast(&swapped)), 2); - } - else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) - { - byte |= 127; - header.append(static_cast(byte)); - quint64 swapped = qToBigEndian(payloadLength); - header.append(static_cast(static_cast(&swapped)), 8); - } - - //Write mask - if (maskingKey != 0) - { - header.append(static_cast(static_cast(&maskingKey)), sizeof(quint32)); - } - } - else - { - //setErrorString("WebSocket::getHeader: payload too big!"); - //Q_EMIT error(QAbstractSocket::DatagramTooLargeError); - qDebug() << "WebSocket::getHeader: payload too big!"; - } - - return header; -} - -/*! - * \internal - */ -qint64 QWebSocket::doWriteFrames(const QByteArray &data, bool isBinary) -{ - const QWebSocketProtocol::OpCode firstOpCode = isBinary ? QWebSocketProtocol::OC_BINARY : QWebSocketProtocol::OC_TEXT; - - int numFrames = data.size() / FRAME_SIZE_IN_BYTES; - QByteArray tmpData(data); - tmpData.detach(); - char *payload = tmpData.data(); - quint64 sizeLeft = static_cast(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); - QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode : QWebSocketProtocol::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) - { - QWebSocketProtocol::mask(currentData, size, maskingKey); - } - qint64 written = m_pSocket->write(currentData, static_cast(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; -} - -/*! - * \internal - */ -quint32 QWebSocket::generateRandomNumber() const -{ - return static_cast((static_cast(qrand()) / RAND_MAX) * std::numeric_limits::max()); -} - -/*! - \internal - */ -quint32 QWebSocket::generateMaskingKey() const -{ - return generateRandomNumber(); -} - -/*! - \internal - */ -QByteArray QWebSocket::generateKey() const -{ - QByteArray key; - - for (int i = 0; i < 4; ++i) - { - quint32 tmp = generateRandomNumber(); - key.append(static_cast(static_cast(&tmp)), sizeof(quint32)); - } - - return key.toBase64(); -} - - -/*! - \internal - */ -QString QWebSocket::calculateAcceptKey(const QString &key) const -{ - QString tmpKey = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - QByteArray hash = QCryptographicHash::hash(tmpKey.toLatin1(), QCryptographicHash::Sha1); - return QString(hash.toBase64()); -} - -/*! - \internal - */ -qint64 QWebSocket::writeFrames(const QList &frames) -{ - qint64 written = 0; - for (int i = 0; i < frames.size(); ++i) - { - written += writeFrame(frames[i]); - } - return written; -} - -/*! - \internal - */ -qint64 QWebSocket::writeFrame(const QByteArray &frame) -{ - return m_pSocket->write(frame); -} - -/*! - \internal - */ -QString readLine(QTcpSocket *pSocket) -{ - QString line; - char c; - while (pSocket->getChar(&c)) - { - if (c == '\r') - { - pSocket->getChar(&c); - break; - } - else - { - line.append(QChar(c)); - } - } - return line; -} - -//called on the client for a server handshake response -/*! - \internal - */ -void QWebSocket::processHandshake(QTcpSocket *pSocket) -{ - if (pSocket == 0) - { - return; - } - - bool ok = false; - QString errorDescription; - - const QString regExpStatusLine("^(HTTP/[0-9]+\\.[0-9]+)\\s([0-9]+)\\s(.*)"); - const QRegularExpression regExp(regExpStatusLine); - QString statusLine = readLine(pSocket); - QString httpProtocol; - int httpStatusCode; - QString httpStatusMessage; - QRegularExpressionMatch match = regExp.match(statusLine); - if (match.hasMatch()) - { - QStringList tokens = match.capturedTexts(); - tokens.removeFirst(); //remove the search string - if (tokens.length() == 3) - { - httpProtocol = tokens[0]; - httpStatusCode = tokens[1].toInt(); - httpStatusMessage = tokens[2].trimmed(); - ok = true; - } - } - if (!ok) - { - errorDescription = "WebSocket::processHandshake: Invalid statusline in response: " + statusLine; - } - else - { - QString headerLine = readLine(pSocket); - QMap headers; - while (!headerLine.isEmpty()) - { - QStringList headerField = headerLine.split(QString(": "), QString::SkipEmptyParts); - headers.insertMulti(headerField[0], headerField[1]); - headerLine = readLine(pSocket); - } - - QString acceptKey = headers.value("Sec-WebSocket-Accept", ""); - QString upgrade = headers.value("Upgrade", ""); - QString connection = headers.value("Connection", ""); - //unused for the moment - //QString extensions = headers.value("Sec-WebSocket-Extensions", ""); - //QString protocol = headers.value("Sec-WebSocket-Protocol", ""); - QString version = headers.value("Sec-WebSocket-Version", ""); - - if (httpStatusCode == 101) //HTTP/x.y 101 Switching Protocols - { - bool conversionOk = false; - float version = httpProtocol.midRef(5).toFloat(&conversionOk); - //TODO: do not check the httpStatusText right now - ok = !(acceptKey.isEmpty() || - (!conversionOk || (version < 1.1f)) || - (upgrade.toLower() != "websocket") || - (connection.toLower() != "upgrade")); - if (ok) - { - QString accept = calculateAcceptKey(m_key); - ok = (accept == acceptKey); - if (!ok) - { - errorDescription = "WebSocket::processHandshake: Accept-Key received from server " + acceptKey + " does not match the client key " + accept; - } - } - else - { - errorDescription = "WebSocket::processHandshake: Invalid statusline in response: " + statusLine; - } - } - else if (httpStatusCode == 400) //HTTP/1.1 400 Bad Request - { - if (!version.isEmpty()) - { - QStringList versions = version.split(", ", QString::SkipEmptyParts); - if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) - { - //if needed to switch protocol version, then we are finished here - //because we cannot handle other protocols than the RFC one (v13) - errorDescription = "WebSocket::processHandshake: Server requests a version that we don't support: " + versions.join(", "); - ok = false; - } - else - { - //we tried v13, but something different went wrong - errorDescription = "WebSocket::processHandshake: Unknown error condition encountered. Aborting connection."; - ok = false; - } - } - } - else - { - errorDescription = "WebSocket::processHandshake: Unhandled http status code " + QString::number(httpStatusCode); - ok = false; - } - - if (!ok) - { - qDebug() << errorDescription; - setErrorString(errorDescription); - Q_EMIT error(QAbstractSocket::ConnectionRefusedError); - } - else - { - //handshake succeeded - setSocketState(QAbstractSocket::ConnectedState); - Q_EMIT connected(); - } - } -} - -/*! - \internal - */ -void QWebSocket::processStateChanged(QAbstractSocket::SocketState socketState) -{ - QAbstractSocket::SocketState webSocketState = this->state(); - switch (socketState) - { - case QAbstractSocket::ConnectedState: - { - if (webSocketState == QAbstractSocket::ConnectingState) - { - m_key = generateKey(); - QString handshake = createHandShakeRequest(m_resourceName, m_requestUrl.host() + ":" + QString::number(m_requestUrl.port(80)), origin(), "", "", m_key); - m_pSocket->write(handshake.toLatin1()); - } - break; - } - case QAbstractSocket::ClosingState: - { - if (webSocketState == QAbstractSocket::ConnectedState) - { - setSocketState(QAbstractSocket::ClosingState); - } - break; - } - case QAbstractSocket::UnconnectedState: - { - if (webSocketState != QAbstractSocket::UnconnectedState) - { - setSocketState(QAbstractSocket::UnconnectedState); - Q_EMIT disconnected(); - } - break; - } - case QAbstractSocket::HostLookupState: - case QAbstractSocket::ConnectingState: - case QAbstractSocket::BoundState: - case QAbstractSocket::ListeningState: - { - //do nothing - //to make C++ compiler happy; - break; - } - default: - { - break; - } - } -} - -//order of events: -//connectToHost is called -//our socket state is set to "connecting", and tcpSocket->connectToHost is called -//the tcpsocket is opened, a handshake message is sent; a readyRead signal is thrown -//this signal is catched by processData -//when OUR socket state is in the "connecting state", this means that -//we have received data from the server (response to handshake), and that we -//should "upgrade" our socket to a websocket (connected state) -//if our socket was already upgraded, then we need to process websocket data -/*! - \internal - */ -void QWebSocket::processData() -{ - while (m_pSocket->bytesAvailable()) - { - if (state() == QAbstractSocket::ConnectingState) - { - processHandshake(m_pSocket); - } - else - { - m_dataProcessor.process(m_pSocket); - } - } -} - -/*! - \internal - */ -void QWebSocket::processControlFrame(QWebSocketProtocol::OpCode opCode, QByteArray frame) -{ - switch (opCode) - { - case QWebSocketProtocol::OC_PING: - { - quint32 maskingKey = 0; - if (m_mustMask) - { - maskingKey = generateMaskingKey(); - } - m_pSocket->write(getFrameHeader(QWebSocketProtocol::OC_PONG, frame.size(), maskingKey, true)); - if (frame.size() > 0) - { - if (m_mustMask) - { - QWebSocketProtocol::mask(&frame, maskingKey); - } - m_pSocket->write(frame); - } - break; - } - case QWebSocketProtocol::OC_PONG: - { - Q_EMIT pong(static_cast(m_pingTimer.elapsed())); - break; - } - case QWebSocketProtocol::OC_CLOSE: - { - quint16 closeCode = QWebSocketProtocol::CC_NORMAL; - QString closeReason; - if (frame.size() > 0) //close frame can have a close code and reason - { - closeCode = qFromBigEndian(reinterpret_cast(frame.constData())); - if (!QWebSocketProtocol::isCloseCodeValid(closeCode)) - { - closeCode = QWebSocketProtocol::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 = QWebSocketProtocol::CC_WRONG_DATATYPE; - closeReason = "Invalid UTF-8 code encountered."; - } - } - } - } - m_isClosingHandshakeReceived = true; - close(static_cast(closeCode), closeReason); - break; - } - case QWebSocketProtocol::OC_CONTINUE: - case QWebSocketProtocol::OC_BINARY: - case QWebSocketProtocol::OC_TEXT: - case QWebSocketProtocol::OC_RESERVED_3: - case QWebSocketProtocol::OC_RESERVED_4: - case QWebSocketProtocol::OC_RESERVED_5: - case QWebSocketProtocol::OC_RESERVED_6: - case QWebSocketProtocol::OC_RESERVED_7: - case QWebSocketProtocol::OC_RESERVED_B: - case QWebSocketProtocol::OC_RESERVED_D: - case QWebSocketProtocol::OC_RESERVED_E: - case QWebSocketProtocol::OC_RESERVED_F: - case QWebSocketProtocol::OC_RESERVED_V: - { - //do nothing - //case added to make C++ compiler happy - break; - } - default: - { - qDebug() << "WebSocket::processData: Invalid opcode detected:" << static_cast(opCode); - //Do nothing - break; - } - } -} - -/*! - \internal - */ -QString QWebSocket::createHandShakeRequest(QString resourceName, - QString host, - QString origin, - QString extensions, - QString protocols, - QByteArray key) -{ - QStringList handshakeRequest; - - handshakeRequest << "GET " + resourceName + " HTTP/1.1" << - "Host: " + host << - "Upgrade: websocket" << - "Connection: Upgrade" << - "Sec-WebSocket-Key: " + QString(key); - if (!origin.isEmpty()) - { - handshakeRequest << "Origin: " + origin; - } - handshakeRequest << "Sec-WebSocket-Version: " + QString::number(QWebSocketProtocol::currentVersion()); - if (extensions.length() > 0) - { - handshakeRequest << "Sec-WebSocket-Extensions: " + extensions; - } - if (protocols.length() > 0) - { - handshakeRequest << "Sec-WebSocket-Protocol: " + protocols; - } - handshakeRequest << "\r\n"; - - return handshakeRequest.join("\r\n"); + return d_ptr->extension(); } /*! @@ -1120,7 +344,7 @@ QString QWebSocket::createHandShakeRequest(QString resourceName, */ QAbstractSocket::SocketState QWebSocket::state() const { - return m_socketState; + return d_ptr->state(); } /** @@ -1145,12 +369,7 @@ QAbstractSocket::SocketState QWebSocket::state() const */ bool QWebSocket::waitForConnected(int msecs) { - bool retVal = false; - if (m_pSocket) - { - retVal = m_pSocket->waitForConnected(msecs); - } - return retVal; + return d_ptr->waitForConnected(msecs); } /*! @@ -1164,35 +383,7 @@ bool QWebSocket::waitForConnected(int msecs) */ bool QWebSocket::waitForDisconnected(int msecs) { - bool retVal = true; - if (m_pSocket) - { - retVal = m_pSocket->waitForDisconnected(msecs); - } - return retVal; -} - -/*! - \internal - Sets the internal socket state -*/ -void QWebSocket::setSocketState(QAbstractSocket::SocketState state) -{ - if (m_socketState != state) - { - m_socketState = state; - Q_EMIT stateChanged(m_socketState); - } -} - -/*! - \internal - Sets the error string. - Only used internally. -*/ -void QWebSocket::setErrorString(QString errorString) -{ - m_errorString = errorString; + return d_ptr->waitForDisconnected(msecs); } /*! @@ -1200,12 +391,7 @@ void QWebSocket::setErrorString(QString errorString) */ QHostAddress QWebSocket::localAddress() const { - QHostAddress address; - if (m_pSocket) - { - address = m_pSocket->localAddress(); - } - return address; + return d_ptr->localAddress(); } /*! @@ -1213,12 +399,7 @@ QHostAddress QWebSocket::localAddress() const */ quint16 QWebSocket::localPort() const { - quint16 port = 0; - if (m_pSocket) - { - port = m_pSocket->localPort(); - } - return port; + return d_ptr->localPort(); } /*! @@ -1226,12 +407,7 @@ quint16 QWebSocket::localPort() const */ QHostAddress QWebSocket::peerAddress() const { - QHostAddress peer; - if (m_pSocket) - { - peer = m_pSocket->peerAddress(); - } - return peer; + return d_ptr->peerAddress(); } /*! @@ -1239,12 +415,7 @@ QHostAddress QWebSocket::peerAddress() const */ QString QWebSocket::peerName() const { - QString name; - if (m_pSocket) - { - name = m_pSocket->peerName(); - } - return name; + return d_ptr->peerName(); } /*! @@ -1252,12 +423,7 @@ QString QWebSocket::peerName() const */ quint16 QWebSocket::peerPort() const { - quint16 port = 0; - if (m_pSocket) - { - port = m_pSocket->peerPort(); - } - return port; + return d_ptr->peerPort(); } /*! @@ -1265,12 +431,7 @@ quint16 QWebSocket::peerPort() const */ QNetworkProxy QWebSocket::proxy() const { - QNetworkProxy proxy; - if (m_pSocket) - { - proxy = m_pSocket->proxy(); - } - return proxy; + return d_ptr->proxy(); } /*! @@ -1278,12 +439,7 @@ QNetworkProxy QWebSocket::proxy() const */ qint64 QWebSocket::readBufferSize() const { - qint64 readBuffer = 0; - if (m_pSocket) - { - readBuffer = m_pSocket->readBufferSize(); - } - return readBuffer; + return d_ptr->readBufferSize(); } /*! @@ -1291,10 +447,7 @@ qint64 QWebSocket::readBufferSize() const */ void QWebSocket::setProxy(const QNetworkProxy &networkProxy) { - if (m_pSocket) - { - m_pSocket->setProxy(networkProxy); - } + d_ptr->setProxy(networkProxy); } /** @@ -1306,10 +459,7 @@ void QWebSocket::setProxy(const QNetworkProxy &networkProxy) */ void QWebSocket::setReadBufferSize(qint64 size) { - if (m_pSocket) - { - m_pSocket->setReadBufferSize(size); - } + d_ptr->setReadBufferSize(size); } /*! @@ -1318,10 +468,7 @@ void QWebSocket::setReadBufferSize(qint64 size) */ void QWebSocket::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value) { - if (m_pSocket) - { - m_pSocket->setSocketOption(option, value); - } + d_ptr->setSocketOption(option, value); } /*! @@ -1330,12 +477,7 @@ void QWebSocket::setSocketOption(QAbstractSocket::SocketOption option, const QVa */ QVariant QWebSocket::socketOption(QAbstractSocket::SocketOption option) { - QVariant result; - if (m_pSocket) - { - result = m_pSocket->socketOption(option); - } - return result; + return d_ptr->socketOption(option); } /*! @@ -1343,10 +485,5 @@ QVariant QWebSocket::socketOption(QAbstractSocket::SocketOption option) */ bool QWebSocket::isValid() { - bool valid = false; - if (m_pSocket) - { - valid = m_pSocket->isValid(); - } - return valid; + return d_ptr->isValid(); } diff --git a/source/qwebsocket.h b/source/qwebsocket.h index cb69297..ed88ba8 100644 --- a/source/qwebsocket.h +++ b/source/qwebsocket.h @@ -18,11 +18,9 @@ #include #include "qwebsocketsglobal.h" #include "qwebsocketprotocol.h" -#include "dataprocessor_p.h" -class HandshakeRequest; -class HandshakeResponse; class QTcpSocket; +class QWebSocketPrivate; class Q_WEBSOCKETS_EXPORT QWebSocket:public QObject { @@ -83,74 +81,12 @@ Q_SIGNALS: void error(QAbstractSocket::SocketError error); void pong(quint64 elapsedTime); -private Q_SLOTS: - void processData(); - void processControlFrame(QWebSocketProtocol::OpCode opCode, QByteArray frame); - void processHandshake(QTcpSocket *pSocket); - void processStateChanged(QAbstractSocket::SocketState socketState); - private: Q_DISABLE_COPY(QWebSocket) - QWebSocket(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version, QObject *parent = 0); - void setVersion(QWebSocketProtocol::Version version); - void setResourceName(QString resourceName); - void setRequestUrl(QUrl requestUrl); - void setOrigin(QString origin); - void setProtocol(QString protocol); - void setExtension(QString extension); - void enableMasking(bool enable); - void setSocketState(QAbstractSocket::SocketState state); - void setErrorString(QString errorString); - - qint64 doWriteData(const QByteArray &data, bool isBinary); - qint64 doWriteFrames(const QByteArray &data, bool isBinary); - - void makeConnections(const QTcpSocket *pTcpSocket); - void releaseConnections(const QTcpSocket *pTcpSocket); - - QByteArray getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength, quint32 maskingKey, bool lastFrame) const; - QString calculateAcceptKey(const QString &key) const; - QString createHandShakeRequest(QString resourceName, - QString host, - QString origin, - QString extensions, - QString protocols, - QByteArray key); - - quint32 generateMaskingKey() const; - QByteArray generateKey() const; - quint32 generateRandomNumber() const; - qint64 writeFrames(const QList &frames); - qint64 writeFrame(const QByteArray &frame); - - static QWebSocket *upgradeFrom(QTcpSocket *tcpSocket, - const HandshakeRequest &request, - const HandshakeResponse &response, - QObject *parent = 0); - friend class QWebSocketServerPrivate; - - QTcpSocket *m_pSocket; - QString m_errorString; - QWebSocketProtocol::Version m_version; - QUrl m_resource; - QString m_resourceName; - QUrl m_requestUrl; - QString m_origin; - QString m_protocol; - QString m_extension; - QAbstractSocket::SocketState m_socketState; - - QByteArray m_key; //identification key used in handshake requests - - bool m_mustMask; //a server must not mask the frames it sends - - bool m_isClosingHandshakeSent; - bool m_isClosingHandshakeReceived; - - QTime m_pingTimer; + QWebSocketPrivate * const d_ptr; - DataProcessor m_dataProcessor; + friend class QWebSocketPrivate; }; #endif // QWEBSOCKET_H diff --git a/source/qwebsocket_p.cpp b/source/qwebsocket_p.cpp new file mode 100644 index 0000000..26e12a0 --- /dev/null +++ b/source/qwebsocket_p.cpp @@ -0,0 +1,1147 @@ +#include "qwebsocket.h" +#include "qwebsocket_p.h" +#include "handshakerequest_p.h" +#include "handshakeresponse_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //maximum size of a frame when sending a message + +/*! + \internal +*/ +QWebSocketPrivate::QWebSocketPrivate(QString origin, QWebSocketProtocol::Version version, QWebSocket *pWebSocket, QObject *parent) : + QObject(parent), + q_ptr(pWebSocket), + m_pSocket(new QTcpSocket(this)), + m_errorString(), + m_version(version), + m_resourceName(), + m_requestUrl(), + m_origin(origin), + m_protocol(""), + m_extension(""), + m_socketState(QAbstractSocket::UnconnectedState), + m_key(), + m_mustMask(true), + m_isClosingHandshakeSent(false), + m_isClosingHandshakeReceived(false), + m_pingTimer(), + m_dataProcessor() +{ + Q_ASSERT(pWebSocket != 0); + makeConnections(m_pSocket); + qsrand(static_cast(QDateTime::currentMSecsSinceEpoch())); +} + +/*! + \internal +*/ +QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version, QWebSocket *pWebSocket, QObject *parent) : + QObject(parent), + q_ptr(pWebSocket), + m_pSocket(pTcpSocket), + m_errorString(pTcpSocket->errorString()), + m_version(version), + m_resourceName(), + m_requestUrl(), + m_origin(), + m_protocol(), + m_extension(), + m_socketState(pTcpSocket->state()), + m_key(), + m_mustMask(true), + m_isClosingHandshakeSent(false), + m_isClosingHandshakeReceived(false), + m_pingTimer(), + m_dataProcessor() +{ + Q_ASSERT(pWebSocket != 0); + makeConnections(m_pSocket); +} + +/*! + \internal +*/ +QWebSocketPrivate::~QWebSocketPrivate() +{ + if (state() == QAbstractSocket::ConnectedState) + { + close(QWebSocketProtocol::CC_GOING_AWAY, "Connection closed"); + } + releaseConnections(m_pSocket); + m_pSocket->deleteLater(); + m_pSocket = 0; +} + +/*! + \internal + */ +void QWebSocketPrivate::abort() +{ + m_pSocket->abort(); +} + +/*! + \internal + */ +QAbstractSocket::SocketError QWebSocketPrivate::error() const +{ + return m_pSocket->error(); +} + +/*! + \internal + */ +QString QWebSocketPrivate::errorString() const +{ + if (!m_errorString.isEmpty()) + { + return m_errorString; + } + else + { + return m_pSocket->errorString(); + } +} + +/*! + \internal + */ +bool QWebSocketPrivate::flush() +{ + return m_pSocket->flush(); +} + +/*! + \internal + */ +qint64 QWebSocketPrivate::send(const char *message) +{ + return send(QString::fromUtf8(message)); +} + +/*! + \internal + */ +qint64 QWebSocketPrivate::send(const QString &message) +{ + return doWriteData(message.toUtf8(), false); +} + +/*! + \internal + */ +qint64 QWebSocketPrivate::send(const QByteArray &data) +{ + return doWriteData(data, true); +} + +/*! + \internal + */ +QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket, + const HandshakeRequest &request, + const HandshakeResponse &response, + QObject *parent) +{ + QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.getAcceptedVersion(), parent); + pWebSocket->d_ptr->setExtension(response.getAcceptedExtension()); + pWebSocket->d_ptr->setOrigin(request.getOrigin()); + pWebSocket->d_ptr->setRequestUrl(request.getRequestUrl()); + pWebSocket->d_ptr->setProtocol(response.getAcceptedProtocol()); + pWebSocket->d_ptr->setResourceName(request.getRequestUrl().toString(QUrl::RemoveUserInfo)); + pWebSocket->d_ptr->enableMasking(false); //a server should not send masked frames + + return pWebSocket; +} + +/*! + \internal + */ +void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason) +{ + if (!m_isClosingHandshakeSent) + { + quint32 maskingKey = 0; + if (m_mustMask) + { + maskingKey = generateMaskingKey(); + } + quint16 code = qToBigEndian(closeCode); + QByteArray payload; + payload.append(static_cast(static_cast(&code)), 2); + if (!reason.isEmpty()) + { + payload.append(reason.toUtf8()); + } + if (m_mustMask) + { + QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey); + } + QByteArray frame = getFrameHeader(QWebSocketProtocol::OC_CLOSE, payload.size(), maskingKey, true); + frame.append(payload); + m_pSocket->write(frame); + m_pSocket->flush(); + + m_isClosingHandshakeSent = true; + + Q_EMIT q_ptr->aboutToClose(); + } + m_pSocket->close(); +} + +/*! + \internal + */ +void QWebSocketPrivate::open(const QUrl &url, bool mask) +{ + m_dataProcessor.clear(); + m_isClosingHandshakeReceived = false; + m_isClosingHandshakeSent = false; + + setRequestUrl(url); + QString resourceName = url.path() + url.query(); + if (resourceName.isEmpty()) + { + resourceName = "/"; + } + setResourceName(resourceName); + enableMasking(mask); + + setSocketState(QAbstractSocket::ConnectingState); + + m_pSocket->connectToHost(url.host(), url.port(80)); +} + +/*! + \internal + */ +void QWebSocketPrivate::ping() +{ + m_pingTimer.restart(); + QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OC_PING, 0, 0, true); + writeFrame(pingFrame); +} + +/*! + \internal + Sets the version to use for the websocket protocol; this must be set before the socket is opened. +*/ +void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version) +{ + m_version = version; +} + +/*! + \internal + Sets the resource name of the connection; must be set before the socket is openend +*/ +void QWebSocketPrivate::setResourceName(QString resourceName) +{ + m_resourceName = resourceName; +} + +/*! + \internal + */ +void QWebSocketPrivate::setRequestUrl(QUrl requestUrl) +{ + m_requestUrl = requestUrl; +} + +/*! + \internal + */ +void QWebSocketPrivate::setOrigin(QString origin) +{ + m_origin = origin; +} + +/*! + \internal + */ +void QWebSocketPrivate::setProtocol(QString protocol) +{ + m_protocol = protocol; +} + +/*! + \internal + */ +void QWebSocketPrivate::setExtension(QString extension) +{ + m_extension = extension; +} + +/*! + \internal + */ +void QWebSocketPrivate::enableMasking(bool enable) +{ + m_mustMask = enable; +} + +/*! + * \internal + */ +qint64 QWebSocketPrivate::doWriteData(const QByteArray &data, bool isBinary) +{ + return doWriteFrames(data, isBinary); +} + +/*! + * \internal + */ +void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket) +{ + //pass through signals + connect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), q_ptr, SIGNAL(error(QAbstractSocket::SocketError))); + connect(pTcpSocket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), q_ptr, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *))); + connect(pTcpSocket, SIGNAL(readChannelFinished()), q_ptr, SIGNAL(readChannelFinished())); + //connect(pTcpSocket, SIGNAL(aboutToClose()), q_ptr, SIGNAL(aboutToClose())); + //connect(pTcpSocket, SIGNAL(bytesWritten(qint64)), q_ptr, SIGNAL(bytesWritten(qint64))); + + //catch signals + connect(pTcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(processStateChanged(QAbstractSocket::SocketState))); + connect(pTcpSocket, SIGNAL(readyRead()), this, SLOT(processData())); + + connect(&m_dataProcessor, SIGNAL(controlFrameReceived(QWebSocketProtocol::OpCode, QByteArray)), this, SLOT(processControlFrame(QWebSocketProtocol::OpCode, QByteArray))); + connect(&m_dataProcessor, SIGNAL(textFrameReceived(QString,bool)), q_ptr, SIGNAL(textFrameReceived(QString,bool))); + connect(&m_dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)), q_ptr, SIGNAL(binaryFrameReceived(QByteArray,bool))); + connect(&m_dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)), q_ptr, SIGNAL(binaryMessageReceived(QByteArray))); + connect(&m_dataProcessor, SIGNAL(textMessageReceived(QString)), q_ptr, SIGNAL(textMessageReceived(QString))); + connect(&m_dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)), this, SLOT(close(QWebSocketProtocol::CloseCode,QString))); +} + +/*! + * \internal + */ +void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket) +{ + if (pTcpSocket) + { + //pass through signals + disconnect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), q_ptr, SIGNAL(error(QAbstractSocket::SocketError))); + disconnect(pTcpSocket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), q_ptr, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *))); + disconnect(pTcpSocket, SIGNAL(readChannelFinished()), q_ptr, SIGNAL(readChannelFinished())); + //disconnect(pTcpSocket, SIGNAL(aboutToClose()), q_ptr, SIGNAL(aboutToClose())); + //disconnect(pTcpSocket, SIGNAL(bytesWritten(qint64)), q_ptr, SIGNAL(bytesWritten(qint64))); + + //catched signals + disconnect(pTcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(processStateChanged(QAbstractSocket::SocketState))); + disconnect(pTcpSocket, SIGNAL(readyRead()), this, SLOT(processData())); + } + disconnect(&m_dataProcessor, SIGNAL(controlFrameReceived(QWebSocketProtocol::OpCode,QByteArray)), this, SLOT(processControlFrame(QWebSocketProtocol::OpCode,QByteArray))); + disconnect(&m_dataProcessor, SIGNAL(textFrameReceived(QString,bool)), q_ptr, SIGNAL(textFrameReceived(QString,bool))); + disconnect(&m_dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)), q_ptr, SIGNAL(binaryFrameReceived(QByteArray,bool))); + disconnect(&m_dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)), q_ptr, SIGNAL(binaryMessageReceived(QByteArray))); + disconnect(&m_dataProcessor, SIGNAL(textMessageReceived(QString)), q_ptr, SIGNAL(textMessageReceived(QString))); + disconnect(&m_dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)), this, SLOT(close(QWebSocketProtocol::CloseCode,QString))); +} + +/*! + \internal + */ +QWebSocketProtocol::Version QWebSocketPrivate::version() +{ + return m_version; +} + +/*! + \internal + */ +QString QWebSocketPrivate::resourceName() +{ + return m_resourceName; +} + +/*! + \internal + */ +QUrl QWebSocketPrivate::requestUrl() +{ + return m_requestUrl; +} + +/*! + \internal + */ +QString QWebSocketPrivate::origin() +{ + return m_origin; +} + +/*! + \internal + */ +QString QWebSocketPrivate::protocol() +{ + return m_protocol; +} + +/*! + \internal + */ +QString QWebSocketPrivate::extension() +{ + return m_extension; +} + +/*! + * \internal + */ +QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength, quint32 maskingKey, bool lastFrame) const +{ + QByteArray header; + quint8 byte = 0x00; + bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL; + + if (ok) + { + //FIN, RSV1-3, opcode + byte = static_cast((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00)); //FIN, opcode + //RSV-1, RSV-2 and RSV-3 are zero + header.append(static_cast(byte)); + + //Now write the masking bit and the payload length byte + byte = 0x00; + if (maskingKey != 0) + { + byte |= 0x80; + } + if (payloadLength <= 125) + { + byte |= static_cast(payloadLength); + header.append(static_cast(byte)); + } + else if (payloadLength <= 0xFFFFU) + { + byte |= 126; + header.append(static_cast(byte)); + quint16 swapped = qToBigEndian(static_cast(payloadLength)); + header.append(static_cast(static_cast(&swapped)), 2); + } + else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) + { + byte |= 127; + header.append(static_cast(byte)); + quint64 swapped = qToBigEndian(payloadLength); + header.append(static_cast(static_cast(&swapped)), 8); + } + + //Write mask + if (maskingKey != 0) + { + header.append(static_cast(static_cast(&maskingKey)), sizeof(quint32)); + } + } + else + { + //setErrorString("WebSocket::getHeader: payload too big!"); + //Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError); + qDebug() << "WebSocket::getHeader: payload too big!"; + } + + return header; +} + +/*! + * \internal + */ +qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary) +{ + const QWebSocketProtocol::OpCode firstOpCode = isBinary ? QWebSocketProtocol::OC_BINARY : QWebSocketProtocol::OC_TEXT; + + int numFrames = data.size() / FRAME_SIZE_IN_BYTES; + QByteArray tmpData(data); + tmpData.detach(); + char *payload = tmpData.data(); + quint64 sizeLeft = static_cast(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); + QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode : QWebSocketProtocol::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) + { + QWebSocketProtocol::mask(currentData, size, maskingKey); + } + qint64 written = m_pSocket->write(currentData, static_cast(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 q_ptr->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 q_ptr->error(QAbstractSocket::NetworkError); + } + return payloadWritten; +} + +/*! + * \internal + */ +quint32 QWebSocketPrivate::generateRandomNumber() const +{ + return static_cast((static_cast(qrand()) / RAND_MAX) * std::numeric_limits::max()); +} + +/*! + \internal + */ +quint32 QWebSocketPrivate::generateMaskingKey() const +{ + return generateRandomNumber(); +} + +/*! + \internal + */ +QByteArray QWebSocketPrivate::generateKey() const +{ + QByteArray key; + + for (int i = 0; i < 4; ++i) + { + quint32 tmp = generateRandomNumber(); + key.append(static_cast(static_cast(&tmp)), sizeof(quint32)); + } + + return key.toBase64(); +} + + +/*! + \internal + */ +QString QWebSocketPrivate::calculateAcceptKey(const QString &key) const +{ + QString tmpKey = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + QByteArray hash = QCryptographicHash::hash(tmpKey.toLatin1(), QCryptographicHash::Sha1); + return QString(hash.toBase64()); +} + +/*! + \internal + */ +qint64 QWebSocketPrivate::writeFrames(const QList &frames) +{ + qint64 written = 0; + for (int i = 0; i < frames.size(); ++i) + { + written += writeFrame(frames[i]); + } + return written; +} + +/*! + \internal + */ +qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame) +{ + return m_pSocket->write(frame); +} + +/*! + \internal + */ +QString readLine(QTcpSocket *pSocket) +{ + QString line; + char c; + while (pSocket->getChar(&c)) + { + if (c == '\r') + { + pSocket->getChar(&c); + break; + } + else + { + line.append(QChar(c)); + } + } + return line; +} + +//called on the client for a server handshake response +/*! + \internal + */ +void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) +{ + if (pSocket == 0) + { + return; + } + + bool ok = false; + QString errorDescription; + + const QString regExpStatusLine("^(HTTP/[0-9]+\\.[0-9]+)\\s([0-9]+)\\s(.*)"); + const QRegularExpression regExp(regExpStatusLine); + QString statusLine = readLine(pSocket); + QString httpProtocol; + int httpStatusCode; + QString httpStatusMessage; + QRegularExpressionMatch match = regExp.match(statusLine); + if (match.hasMatch()) + { + QStringList tokens = match.capturedTexts(); + tokens.removeFirst(); //remove the search string + if (tokens.length() == 3) + { + httpProtocol = tokens[0]; + httpStatusCode = tokens[1].toInt(); + httpStatusMessage = tokens[2].trimmed(); + ok = true; + } + } + if (!ok) + { + errorDescription = "WebSocket::processHandshake: Invalid statusline in response: " + statusLine; + } + else + { + QString headerLine = readLine(pSocket); + QMap headers; + while (!headerLine.isEmpty()) + { + QStringList headerField = headerLine.split(QString(": "), QString::SkipEmptyParts); + headers.insertMulti(headerField[0], headerField[1]); + headerLine = readLine(pSocket); + } + + QString acceptKey = headers.value("Sec-WebSocket-Accept", ""); + QString upgrade = headers.value("Upgrade", ""); + QString connection = headers.value("Connection", ""); + //unused for the moment + //QString extensions = headers.value("Sec-WebSocket-Extensions", ""); + //QString protocol = headers.value("Sec-WebSocket-Protocol", ""); + QString version = headers.value("Sec-WebSocket-Version", ""); + + if (httpStatusCode == 101) //HTTP/x.y 101 Switching Protocols + { + bool conversionOk = false; + float version = httpProtocol.midRef(5).toFloat(&conversionOk); + //TODO: do not check the httpStatusText right now + ok = !(acceptKey.isEmpty() || + (!conversionOk || (version < 1.1f)) || + (upgrade.toLower() != "websocket") || + (connection.toLower() != "upgrade")); + if (ok) + { + QString accept = calculateAcceptKey(m_key); + ok = (accept == acceptKey); + if (!ok) + { + errorDescription = "WebSocket::processHandshake: Accept-Key received from server " + acceptKey + " does not match the client key " + accept; + } + } + else + { + errorDescription = "WebSocket::processHandshake: Invalid statusline in response: " + statusLine; + } + } + else if (httpStatusCode == 400) //HTTP/1.1 400 Bad Request + { + if (!version.isEmpty()) + { + QStringList versions = version.split(", ", QString::SkipEmptyParts); + if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) + { + //if needed to switch protocol version, then we are finished here + //because we cannot handle other protocols than the RFC one (v13) + errorDescription = "WebSocket::processHandshake: Server requests a version that we don't support: " + versions.join(", "); + ok = false; + } + else + { + //we tried v13, but something different went wrong + errorDescription = "WebSocket::processHandshake: Unknown error condition encountered. Aborting connection."; + ok = false; + } + } + } + else + { + errorDescription = "WebSocket::processHandshake: Unhandled http status code " + QString::number(httpStatusCode); + ok = false; + } + + if (!ok) + { + qDebug() << errorDescription; + setErrorString(errorDescription); + Q_EMIT q_ptr->error(QAbstractSocket::ConnectionRefusedError); + } + else + { + //handshake succeeded + setSocketState(QAbstractSocket::ConnectedState); + Q_EMIT q_ptr->connected(); + } + } +} + +/*! + \internal + */ +void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState) +{ + QAbstractSocket::SocketState webSocketState = this->state(); + switch (socketState) + { + case QAbstractSocket::ConnectedState: + { + if (webSocketState == QAbstractSocket::ConnectingState) + { + m_key = generateKey(); + QString handshake = createHandShakeRequest(m_resourceName, m_requestUrl.host() + ":" + QString::number(m_requestUrl.port(80)), origin(), "", "", m_key); + m_pSocket->write(handshake.toLatin1()); + } + break; + } + case QAbstractSocket::ClosingState: + { + if (webSocketState == QAbstractSocket::ConnectedState) + { + setSocketState(QAbstractSocket::ClosingState); + } + break; + } + case QAbstractSocket::UnconnectedState: + { + if (webSocketState != QAbstractSocket::UnconnectedState) + { + setSocketState(QAbstractSocket::UnconnectedState); + Q_EMIT q_ptr->disconnected(); + } + break; + } + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + case QAbstractSocket::BoundState: + case QAbstractSocket::ListeningState: + { + //do nothing + //to make C++ compiler happy; + break; + } + default: + { + break; + } + } +} + +//order of events: +//connectToHost is called +//our socket state is set to "connecting", and tcpSocket->connectToHost is called +//the tcpsocket is opened, a handshake message is sent; a readyRead signal is thrown +//this signal is catched by processData +//when OUR socket state is in the "connecting state", this means that +//we have received data from the server (response to handshake), and that we +//should "upgrade" our socket to a websocket (connected state) +//if our socket was already upgraded, then we need to process websocket data +/*! + \internal + */ +void QWebSocketPrivate::processData() +{ + while (m_pSocket->bytesAvailable()) + { + if (state() == QAbstractSocket::ConnectingState) + { + processHandshake(m_pSocket); + } + else + { + m_dataProcessor.process(m_pSocket); + } + } +} + +/*! + \internal + */ +void QWebSocketPrivate::processControlFrame(QWebSocketProtocol::OpCode opCode, QByteArray frame) +{ + switch (opCode) + { + case QWebSocketProtocol::OC_PING: + { + quint32 maskingKey = 0; + if (m_mustMask) + { + maskingKey = generateMaskingKey(); + } + m_pSocket->write(getFrameHeader(QWebSocketProtocol::OC_PONG, frame.size(), maskingKey, true)); + if (frame.size() > 0) + { + if (m_mustMask) + { + QWebSocketProtocol::mask(&frame, maskingKey); + } + m_pSocket->write(frame); + } + break; + } + case QWebSocketProtocol::OC_PONG: + { + Q_EMIT q_ptr->pong(static_cast(m_pingTimer.elapsed())); + break; + } + case QWebSocketProtocol::OC_CLOSE: + { + quint16 closeCode = QWebSocketProtocol::CC_NORMAL; + QString closeReason; + if (frame.size() > 0) //close frame can have a close code and reason + { + closeCode = qFromBigEndian(reinterpret_cast(frame.constData())); + if (!QWebSocketProtocol::isCloseCodeValid(closeCode)) + { + closeCode = QWebSocketProtocol::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 = QWebSocketProtocol::CC_WRONG_DATATYPE; + closeReason = "Invalid UTF-8 code encountered."; + } + } + } + } + m_isClosingHandshakeReceived = true; + close(static_cast(closeCode), closeReason); + break; + } + case QWebSocketProtocol::OC_CONTINUE: + case QWebSocketProtocol::OC_BINARY: + case QWebSocketProtocol::OC_TEXT: + case QWebSocketProtocol::OC_RESERVED_3: + case QWebSocketProtocol::OC_RESERVED_4: + case QWebSocketProtocol::OC_RESERVED_5: + case QWebSocketProtocol::OC_RESERVED_6: + case QWebSocketProtocol::OC_RESERVED_7: + case QWebSocketProtocol::OC_RESERVED_B: + case QWebSocketProtocol::OC_RESERVED_D: + case QWebSocketProtocol::OC_RESERVED_E: + case QWebSocketProtocol::OC_RESERVED_F: + case QWebSocketProtocol::OC_RESERVED_V: + { + //do nothing + //case added to make C++ compiler happy + break; + } + default: + { + qDebug() << "WebSocket::processData: Invalid opcode detected:" << static_cast(opCode); + //Do nothing + break; + } + } +} + +/*! + \internal + */ +QString QWebSocketPrivate::createHandShakeRequest(QString resourceName, + QString host, + QString origin, + QString extensions, + QString protocols, + QByteArray key) +{ + QStringList handshakeRequest; + + handshakeRequest << "GET " + resourceName + " HTTP/1.1" << + "Host: " + host << + "Upgrade: websocket" << + "Connection: Upgrade" << + "Sec-WebSocket-Key: " + QString(key); + if (!origin.isEmpty()) + { + handshakeRequest << "Origin: " + origin; + } + handshakeRequest << "Sec-WebSocket-Version: " + QString::number(QWebSocketProtocol::currentVersion()); + if (extensions.length() > 0) + { + handshakeRequest << "Sec-WebSocket-Extensions: " + extensions; + } + if (protocols.length() > 0) + { + handshakeRequest << "Sec-WebSocket-Protocol: " + protocols; + } + handshakeRequest << "\r\n"; + + return handshakeRequest.join("\r\n"); +} + +/*! + \internal + */ +QAbstractSocket::SocketState QWebSocketPrivate::state() const +{ + return m_socketState; +} + +/*! + \internal + */ +bool QWebSocketPrivate::waitForConnected(int msecs) +{ + bool retVal = false; + if (m_pSocket) + { + retVal = m_pSocket->waitForConnected(msecs); + } + return retVal; +} + +/*! + \internal + */ +bool QWebSocketPrivate::waitForDisconnected(int msecs) +{ + bool retVal = true; + if (m_pSocket) + { + retVal = m_pSocket->waitForDisconnected(msecs); + } + return retVal; +} + +/*! + \internal + */ +void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state) +{ + if (m_socketState != state) + { + m_socketState = state; + Q_EMIT q_ptr->stateChanged(m_socketState); + } +} + +/*! + \internal + */ +void QWebSocketPrivate::setErrorString(QString errorString) +{ + m_errorString = errorString; +} + +/*! + \internal + */ +QHostAddress QWebSocketPrivate::localAddress() const +{ + QHostAddress address; + if (m_pSocket) + { + address = m_pSocket->localAddress(); + } + return address; +} + +/*! + \internal + */ +quint16 QWebSocketPrivate::localPort() const +{ + quint16 port = 0; + if (m_pSocket) + { + port = m_pSocket->localPort(); + } + return port; +} + +/*! + \internal + */ +QHostAddress QWebSocketPrivate::peerAddress() const +{ + QHostAddress peer; + if (m_pSocket) + { + peer = m_pSocket->peerAddress(); + } + return peer; +} + +/*! + \internal + */ +QString QWebSocketPrivate::peerName() const +{ + QString name; + if (m_pSocket) + { + name = m_pSocket->peerName(); + } + return name; +} + +/*! + \internal + */ +quint16 QWebSocketPrivate::peerPort() const +{ + quint16 port = 0; + if (m_pSocket) + { + port = m_pSocket->peerPort(); + } + return port; +} + +/*! + \internal + */ +QNetworkProxy QWebSocketPrivate::proxy() const +{ + QNetworkProxy proxy; + if (m_pSocket) + { + proxy = m_pSocket->proxy(); + } + return proxy; +} + +/*! + \internal + */ +qint64 QWebSocketPrivate::readBufferSize() const +{ + qint64 readBuffer = 0; + if (m_pSocket) + { + readBuffer = m_pSocket->readBufferSize(); + } + return readBuffer; +} + +/*! + \internal + */ +void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy) +{ + if (m_pSocket) + { + m_pSocket->setProxy(networkProxy); + } +} + +/*! + \internal + */ +void QWebSocketPrivate::setReadBufferSize(qint64 size) +{ + if (m_pSocket) + { + m_pSocket->setReadBufferSize(size); + } +} + +/*! + \internal + */ +void QWebSocketPrivate::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value) +{ + if (m_pSocket) + { + m_pSocket->setSocketOption(option, value); + } +} + +/*! + \internal + */ +QVariant QWebSocketPrivate::socketOption(QAbstractSocket::SocketOption option) +{ + QVariant result; + if (m_pSocket) + { + result = m_pSocket->socketOption(option); + } + return result; +} + +/*! + \internal + */ +bool QWebSocketPrivate::isValid() +{ + bool valid = false; + if (m_pSocket) + { + valid = m_pSocket->isValid(); + } + return valid; +} diff --git a/source/qwebsocket_p.h b/source/qwebsocket_p.h new file mode 100644 index 0000000..72a8d94 --- /dev/null +++ b/source/qwebsocket_p.h @@ -0,0 +1,165 @@ +/** + * @file websocket.h + * @brief Defines the WebSocket class. + * + * \note Currently, only V13 (RFC6455) is supported. + * \note Both text and binary websockets are supported. + * \note The secure version (wss) is currently not implemented. + * @author Kurt Pattyn (pattyn.kurt@gmail.com) + */ + +#ifndef QWEBSOCKET_P_H +#define QWEBSOCKET_P_H + +#include +#include +#include +#include +#include +#include "qwebsocketsglobal.h" +#include "qwebsocketprotocol.h" +#include "dataprocessor_p.h" + +class HandshakeRequest; +class HandshakeResponse; +class QTcpSocket; +class QWebSocket; + +class QWebSocketPrivate:public QObject +{ + Q_OBJECT + +public: + explicit QWebSocketPrivate(QString origin, + QWebSocketProtocol::Version version, + QWebSocket * const pWebSocket, + QObject *parent = 0); + virtual ~QWebSocketPrivate(); + + void abort(); + QAbstractSocket::SocketError error() const; + QString errorString() const; + bool flush(); + bool isValid(); + QHostAddress localAddress() const; + quint16 localPort() const; + QHostAddress peerAddress() const; + QString peerName() const; + quint16 peerPort() const; + QNetworkProxy proxy() const; + qint64 readBufferSize() const; + void setProxy(const QNetworkProxy &networkProxy); + void setReadBufferSize(qint64 size); + void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value); + QVariant socketOption(QAbstractSocket::SocketOption option); + QAbstractSocket::SocketState state() const; + + bool waitForConnected(int msecs = 30000); + bool waitForDisconnected(int msecs = 30000); + + QWebSocketProtocol::Version version(); + QString resourceName(); + QUrl requestUrl(); + QString origin(); + QString protocol(); + QString extension(); + + qint64 send(const char *message); + qint64 send(const QString &message); //send data as text + qint64 send(const QByteArray &data); //send data as binary + +public Q_SLOTS: + virtual void close(QWebSocketProtocol::CloseCode closeCode = QWebSocketProtocol::CC_NORMAL, QString reason = QString()); + virtual void open(const QUrl &url, bool mask = true); + void ping(); + +Q_SIGNALS: + void aboutToClose(); + void connected(); + void disconnected(); + void stateChanged(QAbstractSocket::SocketState state); + void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *pAuthenticator); + void readChannelFinished(); + void textFrameReceived(QString frame, bool isLastFrame); + void binaryFrameReceived(QByteArray frame, bool isLastFrame); + void textMessageReceived(QString message); + void binaryMessageReceived(QByteArray message); + void error(QAbstractSocket::SocketError error); + void pong(quint64 elapsedTime); + +private Q_SLOTS: + void processData(); + void processControlFrame(QWebSocketProtocol::OpCode opCode, QByteArray frame); + void processHandshake(QTcpSocket *pSocket); + void processStateChanged(QAbstractSocket::SocketState socketState); + +private: + Q_DISABLE_COPY(QWebSocketPrivate) + + QWebSocket * const q_ptr; + + QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version, QWebSocket *pWebSocket, QObject *parent = 0); + void setVersion(QWebSocketProtocol::Version version); + void setResourceName(QString resourceName); + void setRequestUrl(QUrl requestUrl); + void setOrigin(QString origin); + void setProtocol(QString protocol); + void setExtension(QString extension); + void enableMasking(bool enable); + void setSocketState(QAbstractSocket::SocketState state); + void setErrorString(QString errorString); + + qint64 doWriteData(const QByteArray &data, bool isBinary); + qint64 doWriteFrames(const QByteArray &data, bool isBinary); + + void makeConnections(const QTcpSocket *pTcpSocket); + void releaseConnections(const QTcpSocket *pTcpSocket); + + QByteArray getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength, quint32 maskingKey, bool lastFrame) const; + QString calculateAcceptKey(const QString &key) const; + QString createHandShakeRequest(QString resourceName, + QString host, + QString origin, + QString extensions, + QString protocols, + QByteArray key); + + static QWebSocket *upgradeFrom(QTcpSocket *tcpSocket, + const HandshakeRequest &request, + const HandshakeResponse &response, + QObject *parent = 0); + + quint32 generateMaskingKey() const; + QByteArray generateKey() const; + quint32 generateRandomNumber() const; + qint64 writeFrames(const QList &frames); + qint64 writeFrame(const QByteArray &frame); + + QTcpSocket *m_pSocket; + QString m_errorString; + QWebSocketProtocol::Version m_version; + QUrl m_resource; + QString m_resourceName; + QUrl m_requestUrl; + QString m_origin; + QString m_protocol; + QString m_extension; + QAbstractSocket::SocketState m_socketState; + + QByteArray m_key; //identification key used in handshake requests + + bool m_mustMask; //a server must not mask the frames it sends + + bool m_isClosingHandshakeSent; + bool m_isClosingHandshakeReceived; + + QTime m_pingTimer; + + DataProcessor m_dataProcessor; + + + friend class QWebSocketServerPrivate; + friend class QWebSocket; +}; + +#endif // QWEBSOCKET_H diff --git a/source/qwebsocketserver_p.cpp b/source/qwebsocketserver_p.cpp index db39fa5..5836029 100644 --- a/source/qwebsocketserver_p.cpp +++ b/source/qwebsocketserver_p.cpp @@ -7,6 +7,7 @@ #include "handshakerequest_p.h" #include "handshakeresponse_p.h" #include "qwebsocket.h" +#include "qwebsocket_p.h" QWebSocketServerPrivate::QWebSocketServerPrivate(const QString &serverName, QWebSocketServer * const pWebSocketServer, QObject *parent) : QObject(parent), @@ -185,7 +186,7 @@ void QWebSocketServerPrivate::handshakeReceived() if (response.canUpgrade()) { - QWebSocket *pWebSocket = QWebSocket::upgradeFrom(pTcpSocket, request, response); + QWebSocket *pWebSocket = QWebSocketPrivate::upgradeFrom(pTcpSocket, request, response); if (pWebSocket) { pWebSocket->setParent(this); diff --git a/source/websocket.pri b/source/websocket.pri index 5f51ba9..55d1e79 100644 --- a/source/websocket.pri +++ b/source/websocket.pri @@ -1,6 +1,8 @@ QT *= network -SOURCES += $$PWD/qwebsocket.cpp \ +SOURCES += \ + $$PWD/qwebsocket.cpp \ + $$PWD/qwebsocket_p.cpp \ $$PWD/qwebsocketserver.cpp \ $$PWD/qwebsocketserver_p.cpp \ $$PWD/qwebsocketprotocol.cpp \ @@ -8,7 +10,9 @@ SOURCES += $$PWD/qwebsocket.cpp \ $$PWD/handshakeresponse_p.cpp \ $$PWD/dataprocessor_p.cpp -HEADERS += $$PWD/qwebsocket.h \ +HEADERS += \ + $$PWD/qwebsocket.h \ + $$PWD/qwebsocket_p.h \ $$PWD/qwebsocketserver.h \ $$PWD/qwebsocketserver_p.h \ $$PWD/qwebsocketprotocol.h \ -- cgit v1.2.1