summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@digia.com>2014-10-21 11:57:37 +0200
committerAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2014-11-19 16:40:39 +0100
commit06df2dac55dfac912b7eb1cbb6c29f2b0594ae10 (patch)
tree944282332b22a6a26b1930ab7f0c3cd09fdd654a /src
parentc47ce34597b085b6c6ab03a12e2ed6b246f330ff (diff)
downloadqtwebsockets-06df2dac55dfac912b7eb1cbb6c29f2b0594ae10.tar.gz
Make the websocket handshake statefull
The websocket handshake could not handle being split over multiple TCP packets since it was entirely handled in on function. This patch splits it into a socket state making it possible to process over multiple incoming packaets. Change-Id: I3c45892ee6f1bb67062d561e9fbd2d7296f1208e Task-number: QTBUG-40878 Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/websockets/qwebsocket_p.cpp108
-rw-r--r--src/websockets/qwebsocket_p.h13
2 files changed, 82 insertions, 39 deletions
diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp
index 602a939..ac743b6 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 f13e08d..9d4ba1c 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;
};