diff options
author | Mårten Nordheim <marten.nordheim@qt.io> | 2022-11-15 14:12:34 +0100 |
---|---|---|
committer | Mårten Nordheim <marten.nordheim@qt.io> | 2022-11-24 09:39:00 +0000 |
commit | 69e2b30057003c0ed17c3478d60fea249546a168 (patch) | |
tree | 34f8b7bdc7d23e55bfa5a6ddda1b6ff781e599be | |
parent | 0fbd3decd0dd694063edd06a174f137ec30d3741 (diff) | |
download | qtwebsockets-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.cpp | 30 | ||||
-rw-r--r-- | src/websockets/qwebsocket_p.h | 2 | ||||
-rw-r--r-- | src/websockets/qwebsocket_wasm_p.cpp | 9 | ||||
-rw-r--r-- | tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp | 28 |
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() |