summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2022-11-15 14:12:34 +0100
committerMårten Nordheim <marten.nordheim@qt.io>2022-11-24 09:39:00 +0000
commit69e2b30057003c0ed17c3478d60fea249546a168 (patch)
tree34f8b7bdc7d23e55bfa5a6ddda1b6ff781e599be
parent0fbd3decd0dd694063edd06a174f137ec30d3741 (diff)
downloadqtwebsockets-69e2b30057003c0ed17c3478d60fea249546a168.tar.gz
QWebSocket: honor subprotocols specified with setRawHeader
We would error out with a ConnectionRejected if the server accepted one of the protocols specified directly in the header since we did not consider those at all. Fixes: QTBUG-108276 Pick-to: 6.4 Change-Id: Ifbb316c9d4871fd764e03c74caefa10f5b757155 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
-rw-r--r--src/websockets/qwebsocket_p.cpp30
-rw-r--r--src/websockets/qwebsocket_p.h2
-rw-r--r--src/websockets/qwebsocket_wasm_p.cpp9
-rw-r--r--tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp28
4 files changed, 50 insertions, 19 deletions
diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp
index 294e361..4be504e 100644
--- a/src/websockets/qwebsocket_p.cpp
+++ b/src/websockets/qwebsocket_p.cpp
@@ -318,6 +318,24 @@ void QWebSocketPrivate::_q_updateSslConfiguration()
#endif
+QStringList QWebSocketPrivate::requestedSubProtocols() const
+{
+ auto subprotocolsRequestedInRawHeader = [this]() {
+ QStringList protocols;
+ QByteArray rawProtocols = m_request.rawHeader("Sec-WebSocket-Protocol");
+ QLatin1StringView rawProtocolsView(rawProtocols);
+ const QStringList &optionsProtocols = m_options.subprotocols();
+ for (auto &&entry : rawProtocolsView.tokenize(u',', Qt::SkipEmptyParts)) {
+ if (QLatin1StringView trimmed = entry.trimmed(); !trimmed.isEmpty()) {
+ if (!optionsProtocols.contains(trimmed))
+ protocols << trimmed;
+ }
+ }
+ return protocols;
+ };
+ return m_options.subprotocols() + subprotocolsRequestedInRawHeader();
+}
+
/*!
Called from QWebSocketServer
\internal
@@ -1002,8 +1020,7 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
#endif
const QString protocol = QString::fromLatin1(parser.combinedHeaderValue(
QByteArrayLiteral("sec-websocket-protocol")));
-
- if (!protocol.isEmpty() && !handshakeOptions().subprotocols().contains(protocol)) {
+ if (!protocol.isEmpty() && !requestedSubProtocols().contains(protocol)) {
setErrorString(QWebSocket::tr("WebSocket server has chosen protocol %1 which has not been "
"requested")
.arg(protocol));
@@ -1096,9 +1113,14 @@ void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketS
QList<QPair<QString, QString> > headers;
const auto headerList = m_request.rawHeaderList();
- for (const QByteArray &key : headerList)
+ for (const QByteArray &key : headerList) {
+ // protocols handled separately below
+ if (key.compare("Sec-WebSocket-Protocol", Qt::CaseInsensitive) == 0)
+ continue;
headers << qMakePair(QString::fromLatin1(key),
QString::fromLatin1(m_request.rawHeader(key)));
+ }
+ const QStringList subProtocols = requestedSubProtocols();
const auto format = QUrl::RemoveScheme | QUrl::RemoveUserInfo
| QUrl::RemovePath | QUrl::RemoveQuery
@@ -1108,7 +1130,7 @@ void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketS
host,
origin(),
QString(),
- m_options.subprotocols(),
+ subProtocols,
m_key,
headers);
if (handshake.isEmpty()) {
diff --git a/src/websockets/qwebsocket_p.h b/src/websockets/qwebsocket_p.h
index 08770e4..08be774 100644
--- a/src/websockets/qwebsocket_p.h
+++ b/src/websockets/qwebsocket_p.h
@@ -152,6 +152,8 @@ private:
void enableMasking(bool enable);
void setErrorString(const QString &errorString);
+ QStringList requestedSubProtocols() const;
+
void socketDestroyed(QObject *socket);
void processData();
diff --git a/src/websockets/qwebsocket_wasm_p.cpp b/src/websockets/qwebsocket_wasm_p.cpp
index e0b2156..4255203 100644
--- a/src/websockets/qwebsocket_wasm_p.cpp
+++ b/src/websockets/qwebsocket_wasm_p.cpp
@@ -174,15 +174,8 @@ void QWebSocketPrivate::open(const QNetworkRequest &request,
// required for some use cases like MQTT.
// add user subprotocol options
- QStringList protocols = handshakeOptions().subprotocols();
-
+ QStringList protocols = requestedSubProtocols();
QByteArray secProto;
- if (request.hasRawHeader("Sec-WebSocket-Protocol")) {
- secProto = request.rawHeader("Sec-WebSocket-Protocol");
- if (!protocols.contains(secProto)) {
- protocols.append(QString::fromLatin1(secProto));
- }
- }
if (!protocols.isEmpty()) {
// comma-separated list of protocol strings, no spaces
secProto = protocols.join(QStringLiteral(",")).toLatin1();
diff --git a/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp b/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
index 118a5c2..c0ba30c 100644
--- a/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
+++ b/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
@@ -356,20 +356,28 @@ void tst_QWebSocketServer::tst_connectivity()
void tst_QWebSocketServer::tst_protocols_data()
{
QTest::addColumn<QStringList>("clientProtocols");
+ QTest::addColumn<QStringList>("headerProtocols");
QTest::addColumn<QString>("expectedProtocol");
- QTest::addRow("none") << QStringList {} << QString {};
+
+ QTest::addRow("none") << QStringList{} << QStringList{} << QString{};
QTest::addRow("same order as server")
- << QStringList { "chat", "superchat" } << QStringLiteral("chat");
+ << QStringList{ "chat", "superchat" } << QStringList{} << QStringLiteral("chat");
QTest::addRow("different order from server")
- << QStringList { "superchat", "chat" } << QStringLiteral("superchat");
- QTest::addRow("unsupported protocol") << QStringList { "foo" } << QString {};
+ << QStringList{ "superchat", "chat" } << QStringList{} << QStringLiteral("superchat");
+ QTest::addRow("unsupported protocol") << QStringList{ "foo" } << QStringList{} << QString{};
QTest::addRow("mixed supported/unsupported protocol")
- << QStringList { "foo", "chat" } << QStringLiteral("chat");
+ << QStringList{ "foo", "chat" } << QStringList{} << QStringLiteral("chat");
+
+ QTest::addRow("same order as server, in header")
+ << QStringList{} << QStringList{ "chat", "superchat" } << QStringLiteral("chat");
+ QTest::addRow("specified in options and in header")
+ << QStringList{ "superchat" } << QStringList{ "chat" } << QStringLiteral("superchat");
}
void tst_QWebSocketServer::tst_protocols()
{
QFETCH(QStringList, clientProtocols);
+ QFETCH(QStringList, headerProtocols);
QFETCH(QString, expectedProtocol);
QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
@@ -380,9 +388,15 @@ void tst_QWebSocketServer::tst_protocols()
QVERIFY(server.listen());
QWebSocketHandshakeOptions opt;
+ QNetworkRequest request(server.serverUrl());
+ if (!headerProtocols.isEmpty()) {
+ QString protocols = headerProtocols.join(u',');
+ request.setRawHeader("Sec-WebSocket-Protocol", protocols.toUtf8());
+ }
opt.setSubprotocols(clientProtocols);
+
QWebSocket client;
- client.open(server.serverUrl(), opt);
+ client.open(request, opt);
QTRY_COMPARE(client.state(), QAbstractSocket::ConnectedState);
QTRY_COMPARE(newConnectionSpy.size(), 1);
@@ -392,7 +406,7 @@ void tst_QWebSocketServer::tst_protocols()
QCOMPARE(client.subprotocol(), expectedProtocol);
QCOMPARE(serverSocket->subprotocol(), expectedProtocol);
- QCOMPARE(serverSocket->handshakeOptions().subprotocols(), clientProtocols);
+ QCOMPARE(serverSocket->handshakeOptions().subprotocols(), clientProtocols + headerProtocols);
}
void tst_QWebSocketServer::tst_preSharedKey()