diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp index 602a939d123e9c972df67ee498dff94f6eb09bf5..ac743b6b4bf49e83dbbdff678187ab9392aafb8c 100644 --- a/src/websockets/qwebsocket_p.cpp +++ b/src/websockets/qwebsocket_p.cpp @@ -137,7 +137,8 @@ QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol: m_dataProcessor(), m_configuration(), m_pMaskGenerator(&m_defaultMaskGenerator), - m_defaultMaskGenerator() + m_defaultMaskGenerator(), + m_handshakeState(NothingDoneState) { } @@ -889,46 +890,66 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) Q_Q(QWebSocket); if (Q_UNLIKELY(!pSocket)) return; + // Reset handshake on a new connection. + if (m_handshakeState == AllDoneState) + m_handshakeState = NothingDoneState; - bool ok = false; QString errorDescription; - const QByteArray statusLine = pSocket->readLine(); - int httpMajorVersion, httpMinorVersion; - int httpStatusCode; - QString httpStatusMessage; - if (Q_UNLIKELY(!parseStatusLine(statusLine, &httpMajorVersion, &httpMinorVersion, - &httpStatusCode, &httpStatusMessage))) { - errorDescription = QWebSocket::tr("Invalid statusline in response: %1.").arg(QString::fromLatin1(statusLine)); - } else { - QString headerLine = readLine(pSocket); - QMap<QString, QString> headers; - while (!headerLine.isEmpty()) { + switch (m_handshakeState) { + case NothingDoneState: + m_headers.clear(); + m_handshakeState = ReadingStatusState; + // no break + case ReadingStatusState: + if (!pSocket->canReadLine()) + return; + m_statusLine = pSocket->readLine(); + if (Q_UNLIKELY(!parseStatusLine(m_statusLine, &m_httpMajorVersion, &m_httpMinorVersion, &m_httpStatusCode, &m_httpStatusMessage))) { + errorDescription = QWebSocket::tr("Invalid statusline in response: %1.").arg(QString::fromLatin1(m_statusLine)); + break; + } + m_handshakeState = ReadingHeaderState; + // no break + case ReadingHeaderState: + while (pSocket->canReadLine()) { + QString headerLine = readLine(pSocket); const QStringList headerField = headerLine.split(QStringLiteral(": "), QString::SkipEmptyParts); if (headerField.size() == 2) { - headers.insertMulti(headerField[0].toLower(), headerField[1]); + m_headers.insertMulti(headerField[0].toLower(), headerField[1]); + } + if (headerField.isEmpty()) { + m_handshakeState = ParsingHeaderState; + break; } - headerLine = readLine(pSocket); } - const QString acceptKey = headers.value(QStringLiteral("sec-websocket-accept"), - QString()); - const QString upgrade = headers.value(QStringLiteral("upgrade"), QString()); - const QString connection = headers.value(QStringLiteral("connection"), QString()); + if (m_handshakeState != ParsingHeaderState) { + if (pSocket->atEnd()) { + errorDescription = QWebSocket::tr("QWebSocketPrivate::processHandshake: Connection closed while reading header."); + break; + } + return; + } + // no break + case ParsingHeaderState: { + const QString acceptKey = m_headers.value(QStringLiteral("sec-websocket-accept"), QString()); + const QString upgrade = m_headers.value(QStringLiteral("upgrade"), QString()); + const QString connection = m_headers.value(QStringLiteral("connection"), QString()); // unused for the moment -// const QString extensions = headers.value(QStringLiteral("sec-websocket-extensions"), +// const QString extensions = m_headers.value(QStringLiteral("sec-websocket-extensions"), // QString()); -// const QString protocol = headers.value(QStringLiteral("sec-websocket-protocol"), +// const QString protocol = m_headers.value(QStringLiteral("sec-websocket-protocol"), // QString()); - const QString version = headers.value(QStringLiteral("sec-websocket-version"), - QString()); + const QString version = m_headers.value(QStringLiteral("sec-websocket-version"), QString()); - if (Q_LIKELY(httpStatusCode == 101)) { + bool ok = false; + if (Q_LIKELY(m_httpStatusCode == 101)) { //HTTP/x.y 101 Switching Protocols //TODO: do not check the httpStatusText right now ok = !(acceptKey.isEmpty() || - (httpMajorVersion < 1 || httpMinorVersion < 1) || + (m_httpMajorVersion < 1 || m_httpMinorVersion < 1) || (upgrade.toLower() != QStringLiteral("websocket")) || (connection.toLower() != QStringLiteral("upgrade"))); if (ok) { @@ -941,9 +962,9 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) } else { errorDescription = QWebSocket::tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.") - .arg(QString::fromLatin1(statusLine)); + .arg(QString::fromLatin1(m_statusLine)); } - } else if (httpStatusCode == 400) { + } else if (m_httpStatusCode == 400) { //HTTP/1.1 400 Bad Request if (!version.isEmpty()) { const QStringList versions = version.split(QStringLiteral(", "), @@ -954,29 +975,38 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) errorDescription = 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 = QWebSocket::tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection."); - ok = false; } + } else { + errorDescription = + QWebSocket::tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection."); } } else { errorDescription = QWebSocket::tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).") - .arg(httpStatusCode).arg(httpStatusMessage); - ok = false; + .arg(m_httpStatusCode).arg(m_httpStatusMessage); } + if (ok) + m_handshakeState = AllDoneState; + break; + } + case AllDoneState: + Q_UNREACHABLE(); + break; + } - if (!ok) { - setErrorString(errorDescription); - Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); - } else { - //handshake succeeded - setSocketState(QAbstractSocket::ConnectedState); - Q_EMIT q->connected(); - } + if (m_handshakeState == AllDoneState) { + // handshake succeeded + setSocketState(QAbstractSocket::ConnectedState); + Q_EMIT q->connected(); + } else { + // handshake failed + m_handshakeState = AllDoneState; + setErrorString(errorDescription); + Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); } } diff --git a/src/websockets/qwebsocket_p.h b/src/websockets/qwebsocket_p.h index f13e08d9fd14280bcfcd0bec4048037ec7b2c156..9d4ba1ce43cec835ce64d3a127626522c869dbfd 100644 --- a/src/websockets/qwebsocket_p.h +++ b/src/websockets/qwebsocket_p.h @@ -224,6 +224,19 @@ private: QMaskGenerator *m_pMaskGenerator; QDefaultMaskGenerator m_defaultMaskGenerator; + enum HandshakeState { + NothingDoneState, + ReadingStatusState, + ReadingHeaderState, + ParsingHeaderState, + AllDoneState + } m_handshakeState; + QByteArray m_statusLine; + int m_httpStatusCode; + int m_httpMajorVersion, m_httpMinorVersion; + QString m_httpStatusMessage; + QMap<QString, QString> m_headers; + friend class QWebSocketServerPrivate; };