Added d-pointer paradigm to QWebSocket implementation
authorKurt Pattyn <pattyn.kurt@gmail.com>
Sat, 24 Aug 2013 21:51:30 +0000 (23:51 +0200)
committerKurt Pattyn <pattyn.kurt@gmail.com>
Sat, 24 Aug 2013 21:51:30 +0000 (23:51 +0200)
source/qwebsocket.cpp
source/qwebsocket.h
source/qwebsocket_p.cpp [new file with mode: 0644]
source/qwebsocket_p.h [new file with mode: 0644]
source/qwebsocketserver_p.cpp
source/websocket.pri

index 8ac59c3..b2e7d22 100644 (file)
@@ -1,15 +1,9 @@
 #include "qwebsocket.h"
-#include "handshakerequest_p.h"
-#include "handshakeresponse_p.h"
+#include "qwebsocket_p.h"
 #include <QUrl>
 #include <QTcpSocket>
 #include <QByteArray>
-#include <QtEndian>
-#include <QCryptographicHash>
-#include <QRegularExpression>
-#include <QStringList>
 #include <QHostAddress>
-#include <QNetworkProxy>
 
 #include <QDebug>
 
@@ -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<uint>(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<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)
-               {
-                       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<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));        //FIN, opcode
-               //RSV-1, RSV-2 and RSV-3 are zero
-               header.append(static_cast<char>(byte));
-
-               //Now write the masking bit and the payload length byte
-               byte = 0x00;
-               if (maskingKey != 0)
-               {
-                       byte |= 0x80;
-               }
-               if (payloadLength <= 125)
-               {
-                       byte |= static_cast<quint8>(payloadLength);
-                       header.append(static_cast<char>(byte));
-               }
-               else if (payloadLength <= 0xFFFFU)
-               {
-                       byte |= 126;
-                       header.append(static_cast<char>(byte));
-                       quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
-                       header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
-               }
-               else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL)
-               {
-                       byte |= 127;
-                       header.append(static_cast<char>(byte));
-                       quint64 swapped = qToBigEndian<quint64>(payloadLength);
-                       header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
-               }
-
-               //Write mask
-               if (maskingKey != 0)
-               {
-                       header.append(static_cast<const char *>(static_cast<const void *>(&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<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);
-               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<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;
-}
-
-/*!
- * \internal
- */
-quint32 QWebSocket::generateRandomNumber() const
-{
-       return static_cast<quint32>((static_cast<double>(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::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<const char *>(static_cast<const void *>(&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<QByteArray> &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<QString, QString> 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<quint64>(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<quint16>(reinterpret_cast<const uchar *>(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<QWebSocketProtocol::CloseCode>(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<int>(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();
 }
index cb69297..ed88ba8 100644 (file)
 #include <QTime>
 #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<QByteArray> &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 (file)
index 0000000..26e12a0
--- /dev/null
@@ -0,0 +1,1147 @@
+#include "qwebsocket.h"
+#include "qwebsocket_p.h"
+#include "handshakerequest_p.h"
+#include "handshakeresponse_p.h"
+#include <QUrl>
+#include <QTcpSocket>
+#include <QByteArray>
+#include <QtEndian>
+#include <QCryptographicHash>
+#include <QRegularExpression>
+#include <QStringList>
+#include <QHostAddress>
+#include <QNetworkProxy>
+
+#include <QDebug>
+
+#include <limits>
+
+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<uint>(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<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)
+               {
+                       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<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));        //FIN, opcode
+               //RSV-1, RSV-2 and RSV-3 are zero
+               header.append(static_cast<char>(byte));
+
+               //Now write the masking bit and the payload length byte
+               byte = 0x00;
+               if (maskingKey != 0)
+               {
+                       byte |= 0x80;
+               }
+               if (payloadLength <= 125)
+               {
+                       byte |= static_cast<quint8>(payloadLength);
+                       header.append(static_cast<char>(byte));
+               }
+               else if (payloadLength <= 0xFFFFU)
+               {
+                       byte |= 126;
+                       header.append(static_cast<char>(byte));
+                       quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
+                       header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
+               }
+               else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL)
+               {
+                       byte |= 127;
+                       header.append(static_cast<char>(byte));
+                       quint64 swapped = qToBigEndian<quint64>(payloadLength);
+                       header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
+               }
+
+               //Write mask
+               if (maskingKey != 0)
+               {
+                       header.append(static_cast<const char *>(static_cast<const void *>(&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<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);
+               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<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 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<quint32>((static_cast<double>(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::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<const char *>(static_cast<const void *>(&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<QByteArray> &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<QString, QString> 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<quint64>(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<quint16>(reinterpret_cast<const uchar *>(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<QWebSocketProtocol::CloseCode>(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<int>(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 (file)
index 0000000..72a8d94
--- /dev/null
@@ -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 <QUrl>
+#include <QAbstractSocket>
+#include <QHostAddress>
+#include <QNetworkProxy>
+#include <QTime>
+#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<QByteArray> &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
index db39fa5..5836029 100644 (file)
@@ -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);
index 5f51ba9..55d1e79 100644 (file)
@@ -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 \