diff options
Diffstat (limited to 'src/websockets/qwebsocket_p.cpp')
-rw-r--r-- | src/websockets/qwebsocket_p.cpp | 122 |
1 files changed, 76 insertions, 46 deletions
diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp index ced197e..2bb5151 100644 --- a/src/websockets/qwebsocket_p.cpp +++ b/src/websockets/qwebsocket_p.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>. -** Contact: http://www.qt-project.org/legal +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtWebSockets module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -137,7 +137,8 @@ QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol: m_dataProcessor(), m_configuration(), m_pMaskGenerator(&m_defaultMaskGenerator), - m_defaultMaskGenerator() + m_defaultMaskGenerator(), + m_handshakeState(NothingDoneState) { } @@ -362,7 +363,7 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask) if (!resourceName.endsWith(QChar::fromLatin1('?'))) { resourceName.append(QChar::fromLatin1('?')); } - resourceName.append(url.query()); + resourceName.append(url.query(QUrl::FullyEncoded)); } if (resourceName.isEmpty()) resourceName = QStringLiteral("/"); @@ -903,46 +904,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) { @@ -955,9 +976,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(", "), @@ -968,29 +989,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); } } |