Fix assertion when encountering invalid header line.
[contrib/qtwebsockets.git] / src / websockets / qwebsocket_p.cpp
index 5d775ef..5f59b54 100644 (file)
@@ -87,11 +87,11 @@ QWebSocketConfiguration::QWebSocketConfiguration() :
     \internal
 */
 QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version,
-                                     QWebSocket *pWebSocket, QObject *parent) :
-    QObject(parent),
+                                     QWebSocket *pWebSocket) :
+    QObjectPrivate(),
     q_ptr(pWebSocket),
     m_pSocket(),
-    m_errorString(),
+    m_errorString(QWebSocket::tr("Unknown error")),
     m_version(version),
     m_resourceName(),
     m_requestUrl(),
@@ -99,6 +99,8 @@ QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::
     m_protocol(),
     m_extension(),
     m_socketState(QAbstractSocket::UnconnectedState),
+    m_pauseMode(QAbstractSocket::PauseNever),
+    m_readBufferSize(0),
     m_key(),
     m_mustMask(true),
     m_isClosingHandshakeSent(false),
@@ -109,15 +111,14 @@ QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::
     m_dataProcessor(),
     m_configuration()
 {
-    init();
 }
 
 /*!
     \internal
 */
 QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version,
-                                     QWebSocket *pWebSocket, QObject *parent) :
-    QObject(parent),
+                                     QWebSocket *pWebSocket) :
+    QObjectPrivate(),
     q_ptr(pWebSocket),
     m_pSocket(pTcpSocket),
     m_errorString(pTcpSocket->errorString()),
@@ -128,6 +129,8 @@ QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol:
     m_protocol(),
     m_extension(),
     m_socketState(pTcpSocket->state()),
+    m_pauseMode(pTcpSocket->pauseMode()),
+    m_readBufferSize(pTcpSocket->readBufferSize()),
     m_key(),
     m_mustMask(true),
     m_isClosingHandshakeSent(false),
@@ -138,8 +141,6 @@ QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol:
     m_dataProcessor(),
     m_configuration()
 {
-    init();
-    makeConnections(m_pSocket.data());
 }
 
 /*!
@@ -148,7 +149,12 @@ QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol:
 void QWebSocketPrivate::init()
 {
     Q_ASSERT(q_ptr);
+    //TODO: need a better randomizer
     qsrand(static_cast<uint>(QDateTime::currentMSecsSinceEpoch()));
+
+    if (m_pSocket) {
+        makeConnections(m_pSocket.data());
+    }
 }
 
 /*!
@@ -159,7 +165,7 @@ QWebSocketPrivate::~QWebSocketPrivate()
     if (!m_pSocket)
         return;
     if (state() == QAbstractSocket::ConnectedState)
-        close(QWebSocketProtocol::CloseCodeGoingAway, tr("Connection closed"));
+        close(QWebSocketProtocol::CloseCodeGoingAway, QWebSocket::tr("Connection closed"));
     releaseConnections(m_pSocket.data());
 }
 
@@ -177,7 +183,7 @@ void QWebSocketPrivate::abort()
  */
 QAbstractSocket::SocketError QWebSocketPrivate::error() const
 {
-    QAbstractSocket::SocketError err = QAbstractSocket::OperationError;
+    QAbstractSocket::SocketError err = QAbstractSocket::UnknownSocketError;
     if (Q_LIKELY(m_pSocket))
         err = m_pSocket->error();
     return err;
@@ -355,19 +361,22 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask)
     #ifndef QT_NO_SSL
         if (url.scheme() == QStringLiteral("wss")) {
             if (!QSslSocket::supportsSsl()) {
-                const QString message = tr("SSL Sockets are not supported on this platform.");
+                const QString message =
+                        QWebSocket::tr("SSL Sockets are not supported on this platform.");
                 setErrorString(message);
                 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
             } else {
-                QSslSocket *sslSocket = new QSslSocket(this);
+                QSslSocket *sslSocket = new QSslSocket(q);
                 m_pSocket.reset(sslSocket);
                 if (Q_LIKELY(m_pSocket)) {
                     m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
                     m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
+                    m_pSocket->setReadBufferSize(m_readBufferSize);
+                    m_pSocket->setPauseMode(m_pauseMode);
 
                     makeConnections(m_pSocket.data());
-                    connect(sslSocket, &QSslSocket::encryptedBytesWritten, q,
-                            &QWebSocket::bytesWritten);
+                    QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten, q,
+                                     &QWebSocket::bytesWritten);
                     setSocketState(QAbstractSocket::ConnectingState);
 
                     sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration);
@@ -380,7 +389,7 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask)
     #endif
                     sslSocket->connectToHostEncrypted(url.host(), url.port(443));
                 } else {
-                    const QString message = tr("Out of memory.");
+                    const QString message = QWebSocket::tr("Out of memory.");
                     setErrorString(message);
                     Q_EMIT q->error(QAbstractSocket::SocketResourceError);
                 }
@@ -388,26 +397,29 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask)
         } else
     #endif
         if (url.scheme() == QStringLiteral("ws")) {
-            m_pSocket.reset(new QTcpSocket(this));
+            m_pSocket.reset(new QTcpSocket(q));
             if (Q_LIKELY(m_pSocket)) {
                 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
                 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
+                m_pSocket->setReadBufferSize(m_readBufferSize);
+                m_pSocket->setPauseMode(m_pauseMode);
 
                 makeConnections(m_pSocket.data());
-                connect(m_pSocket.data(), &QAbstractSocket::bytesWritten, q,
-                        &QWebSocket::bytesWritten);
+                QObject::connect(m_pSocket.data(), &QAbstractSocket::bytesWritten, q,
+                                 &QWebSocket::bytesWritten);
                 setSocketState(QAbstractSocket::ConnectingState);
     #ifndef QT_NO_NETWORKPROXY
                 m_pSocket->setProxy(m_configuration.m_proxy);
     #endif
                 m_pSocket->connectToHost(url.host(), url.port(80));
             } else {
-                const QString message = tr("Out of memory.");
+                const QString message = QWebSocket::tr("Out of memory.");
                 setErrorString(message);
                 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
             }
         } else {
-            const QString message = tr("Unsupported websockets scheme: %1").arg(url.scheme());
+            const QString message =
+                    QWebSocket::tr("Unsupported websockets scheme: %1").arg(url.scheme());
             setErrorString(message);
             Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
         }
@@ -506,40 +518,40 @@ void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
         //pass through signals
         typedef void (QAbstractSocket:: *ASErrorSignal)(QAbstractSocket::SocketError);
         typedef void (QWebSocket:: *WSErrorSignal)(QAbstractSocket::SocketError);
-        connect(pTcpSocket,
-                static_cast<ASErrorSignal>(&QAbstractSocket::error),
-                q, static_cast<WSErrorSignal>(&QWebSocket::error));
-        connect(pTcpSocket, &QAbstractSocket::proxyAuthenticationRequired, q,
-                &QWebSocket::proxyAuthenticationRequired);
-        connect(pTcpSocket, &QAbstractSocket::readChannelFinished, q,
-                &QWebSocket::readChannelFinished);
-        connect(pTcpSocket, &QAbstractSocket::aboutToClose, q, &QWebSocket::aboutToClose);
+        QObject::connect(pTcpSocket,
+                         static_cast<ASErrorSignal>(&QAbstractSocket::error),
+                         q, static_cast<WSErrorSignal>(&QWebSocket::error));
+        QObject::connect(pTcpSocket, &QAbstractSocket::proxyAuthenticationRequired, q,
+                         &QWebSocket::proxyAuthenticationRequired);
+        QObject::connect(pTcpSocket, &QAbstractSocket::readChannelFinished, q,
+                         &QWebSocket::readChannelFinished);
+        QObject::connect(pTcpSocket, &QAbstractSocket::aboutToClose, q, &QWebSocket::aboutToClose);
 
         //catch signals
-        connect(pTcpSocket, &QAbstractSocket::stateChanged, this,
-                &QWebSocketPrivate::processStateChanged);
+        QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::stateChanged, this,
+                                &QWebSocketPrivate::processStateChanged);
         //!!!important to use a QueuedConnection here;
         //with QTcpSocket there is no problem, but with QSslSocket the processing hangs
-        connect(pTcpSocket, &QAbstractSocket::readyRead, this,
-                &QWebSocketPrivate::processData, Qt::QueuedConnection);
+        QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::readyRead, this,
+                                &QWebSocketPrivate::processData, Qt::QueuedConnection);
     }
 
-    connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q,
-            &QWebSocket::textFrameReceived);
-    connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q,
-            &QWebSocket::binaryFrameReceived);
-    connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q,
-            &QWebSocket::binaryMessageReceived);
-    connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q,
-            &QWebSocket::textMessageReceived);
-    connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this,
-            &QWebSocketPrivate::close);
-    connect(&m_dataProcessor, &QWebSocketDataProcessor::pingReceived, this,
-            &QWebSocketPrivate::processPing);
-    connect(&m_dataProcessor, &QWebSocketDataProcessor::pongReceived, this,
-            &QWebSocketPrivate::processPong);
-    connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this,
-            &QWebSocketPrivate::processClose);
+    QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q,
+                     &QWebSocket::textFrameReceived);
+    QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q,
+                     &QWebSocket::binaryFrameReceived);
+    QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q,
+                     &QWebSocket::binaryMessageReceived);
+    QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q,
+                     &QWebSocket::textMessageReceived);
+    QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this,
+                            &QWebSocketPrivate::close);
+    QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pingReceived, this,
+                            &QWebSocketPrivate::processPing);
+    QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pongReceived, this,
+                            &QWebSocketPrivate::processPong);
+    QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this,
+                            &QWebSocketPrivate::processClose);
 }
 
 /*!
@@ -548,8 +560,8 @@ void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
 void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
 {
     if (Q_LIKELY(pTcpSocket))
-        disconnect(pTcpSocket);
-    disconnect(&m_dataProcessor);
+        pTcpSocket->disconnect(pTcpSocket);
+    m_dataProcessor.disconnect(&m_dataProcessor);
 }
 
 /*!
@@ -628,9 +640,8 @@ QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode,
     bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
 
     if (Q_LIKELY(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
+        //FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero)
+        byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));
         header.append(static_cast<char>(byte));
 
         byte = 0x00;
@@ -670,7 +681,7 @@ QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode,
 qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
 {
     qint64 payloadWritten = 0;
-    if (Q_UNLIKELY(!m_pSocket))
+    if (Q_UNLIKELY(!m_pSocket) || (state() != QAbstractSocket::ConnectedState))
         return payloadWritten;
 
     Q_Q(QWebSocket);
@@ -719,7 +730,7 @@ qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
                 payloadWritten += written;
             } else {
                 m_pSocket->flush();
-                setErrorString(tr("Error writing bytes to socket: %1.")
+                setErrorString(QWebSocket::tr("Error writing bytes to socket: %1.")
                                .arg(m_pSocket->errorString()));
                 Q_EMIT q->error(QAbstractSocket::NetworkError);
                 break;
@@ -729,7 +740,8 @@ qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
         bytesLeft -= size;
     }
     if (Q_UNLIKELY(payloadWritten != data.size())) {
-        setErrorString(tr("Bytes written %1 != %2.").arg(payloadWritten).arg(data.size()));
+        setErrorString(QWebSocket::tr("Bytes written %1 != %2.")
+                       .arg(payloadWritten).arg(data.size()));
         Q_EMIT q->error(QAbstractSocket::NetworkError);
     }
     return payloadWritten;
@@ -740,6 +752,7 @@ qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
  */
 quint32 QWebSocketPrivate::generateRandomNumber() const
 {
+    //TODO: need a better randomizer
     return quint32((double(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::max());
 }
 
@@ -852,14 +865,16 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
         }
     }
     if (Q_UNLIKELY(!ok)) {
-        errorDescription = tr("Invalid statusline in response: %1.").arg(statusLine);
+        errorDescription = QWebSocket::tr("Invalid statusline in response: %1.").arg(statusLine);
     } else {
         QString headerLine = readLine(pSocket);
         QMap<QString, QString> headers;
         while (!headerLine.isEmpty()) {
             const QStringList headerField = headerLine.split(QStringLiteral(": "),
                                                              QString::SkipEmptyParts);
-            headers.insertMulti(headerField[0], headerField[1]);
+            if (headerField.size() == 2) {
+                headers.insertMulti(headerField[0], headerField[1]);
+            }
             headerLine = readLine(pSocket);
         }
 
@@ -889,11 +904,11 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
                 ok = (accept == acceptKey);
                 if (!ok)
                     errorDescription =
-                      tr("Accept-Key received from server %1 does not match the client key %2.")
+                      QWebSocket::tr("Accept-Key received from server %1 does not match the client key %2.")
                             .arg(acceptKey).arg(accept);
             } else {
                 errorDescription =
-                    tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.")
+                    QWebSocket::tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.")
                         .arg(statusLine);
             }
         } else if (httpStatusCode == 400) {
@@ -905,21 +920,20 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
                     //if needed to switch protocol version, then we are finished here
                     //because we cannot handle other protocols than the RFC one (v13)
                     errorDescription =
-                            tr("Handshake: Server requests a version that we don't support: %1.")
+                            QWebSocket::tr("Handshake: Server requests a version that we don't support: %1.")
                             .arg(versions.join(QStringLiteral(", ")));
                     ok = false;
                 } else {
                     //we tried v13, but something different went wrong
                     errorDescription =
-                        tr("QWebSocketPrivate::processHandshake: Unknown error condition " \
-                           "encountered. Aborting connection.");
+                        QWebSocket::tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
                     ok = false;
                 }
             }
         } else {
             errorDescription =
-                    tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).")
-                    .arg(httpStatusCode).arg(httpStatusMessage);
+                    QWebSocket::tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).")
+                        .arg(httpStatusCode).arg(httpStatusMessage);
             ok = false;
         }
 
@@ -1119,10 +1133,7 @@ quint16 QWebSocketPrivate::localPort() const
  */
 QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
 {
-    QAbstractSocket::PauseModes mode = QAbstractSocket::PauseNever;
-    if (Q_LIKELY(m_pSocket))
-        mode = m_pSocket->pauseMode();
-    return mode;
+    return m_pauseMode;
 }
 
 /*!
@@ -1182,10 +1193,7 @@ void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
  */
 qint64 QWebSocketPrivate::readBufferSize() const
 {
-    qint64 size = 0;
-    if (Q_LIKELY(m_pSocket))
-        size = m_pSocket->readBufferSize();
-    return size;
+    return m_readBufferSize;
 }
 
 /*!
@@ -1202,8 +1210,9 @@ void QWebSocketPrivate::resume()
  */
 void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
 {
+    m_pauseMode = pauseMode;
     if (Q_LIKELY(m_pSocket))
-        m_pSocket->setPauseMode(pauseMode);
+        m_pSocket->setPauseMode(m_pauseMode);
 }
 
 /*!
@@ -1211,28 +1220,9 @@ void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
  */
 void QWebSocketPrivate::setReadBufferSize(qint64 size)
 {
+    m_readBufferSize = size;
     if (Q_LIKELY(m_pSocket))
-        m_pSocket->setReadBufferSize(size);
-}
-
-/*!
-    \internal
- */
-void QWebSocketPrivate::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
-{
-    if (Q_LIKELY(m_pSocket))
-        m_pSocket->setSocketOption(option, value);
-}
-
-/*!
-    \internal
- */
-QVariant QWebSocketPrivate::socketOption(QAbstractSocket::SocketOption option)
-{
-    QVariant val;
-    if (Q_LIKELY(m_pSocket))
-        val = m_pSocket->socketOption(option);
-    return val;
+        m_pSocket->setReadBufferSize(m_readBufferSize);
 }
 
 /*!
@@ -1240,7 +1230,8 @@ QVariant QWebSocketPrivate::socketOption(QAbstractSocket::SocketOption option)
  */
 bool QWebSocketPrivate::isValid() const
 {
-    return (m_pSocket && m_pSocket->isValid());
+    return (m_pSocket && m_pSocket->isValid() &&
+            (m_socketState == QAbstractSocket::ConnectedState));
 }
 
 QT_END_NAMESPACE