aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorArno Rehn <a.rehn@menlosystems.com>2022-01-02 22:31:03 +0100
committerArno Rehn <a.rehn@menlosystems.com>2022-03-30 17:45:18 +0200
commitcc4c24b99a87629aeb60df5af96d9bb991b56635 (patch)
tree62459a42457b0680022bb1fcb759f9c4e64d2c4b /tests
parent8545bb57efbfabf0dc7bc4b76efd6a99b4022669 (diff)
Add support for WebSocket Sub-Protocols
Sub-Protocol support follows RFC 6455 Sections 4.1 and 4.2. See also https://datatracker.ietf.org/doc/html/rfc6455. This patch introduces a new class QWebSocketHandshakeOptions which collects options for the WebSocket handshake. At the moment, this contains only accessors for sub-protocols. In the future, it can be extended with things like WebSocket extensions. [ChangeLog] Add support for WebSocket Sub-Protocols Fixes: QTBUG-38742 Change-Id: Ibdcef17f717f0a76caab54f65c550865df1ec78d Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/qml/qmlwebsockets/tst_qmlwebsockets.qml7
-rw-r--r--tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp69
-rw-r--r--tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp45
3 files changed, 120 insertions, 1 deletions
diff --git a/tests/auto/qml/qmlwebsockets/tst_qmlwebsockets.qml b/tests/auto/qml/qmlwebsockets/tst_qmlwebsockets.qml
index 66c1ed0..21d3bf6 100644
--- a/tests/auto/qml/qmlwebsockets/tst_qmlwebsockets.qml
+++ b/tests/auto/qml/qmlwebsockets/tst_qmlwebsockets.qml
@@ -38,6 +38,8 @@ Rectangle {
id: server
port: 1337
+ supportedSubprotocols: [ "chat", "superchat" ]
+
onClientConnected: {
currentSocket = webSocket;
}
@@ -48,6 +50,7 @@ Rectangle {
WebSocket {
id: socket
url: server.url
+ requestedSubprotocols: [ "superchat", "chat" ]
}
TestCase {
@@ -56,6 +59,10 @@ Rectangle {
socket.active = true;
tryCompare(socket, 'status', WebSocket.Open);
verify(server.currentSocket);
+
+ // Handshake should select client's first preference.
+ compare(socket.negotiatedSubprotocol, "superchat")
+ compare(server.currentSocket.negotiatedSubprotocol, "superchat")
}
function ensureDisconnected() {
diff --git a/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp b/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp
index 329419c..4f85430 100644
--- a/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp
+++ b/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp
@@ -25,9 +25,11 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+#include <QRegularExpression>
#include <QString>
#include <QtTest>
#include <QtWebSockets/QWebSocket>
+#include <QtWebSockets/QWebSocketHandshakeOptions>
#include <QtWebSockets/QWebSocketServer>
#include <QtWebSockets/qwebsocketprotocol.h>
@@ -72,6 +74,8 @@ EchoServer::EchoServer(QObject *parent, quint64 maxAllowedIncomingMessageSize, q
m_maxAllowedIncomingFrameSize(maxAllowedIncomingFrameSize),
m_clients()
{
+ m_pWebSocketServer->setSupportedSubprotocols({ QStringLiteral("protocol1"),
+ QStringLiteral("protocol2") });
if (m_pWebSocketServer->listen(QHostAddress(QStringLiteral("127.0.0.1")))) {
connect(m_pWebSocketServer, SIGNAL(newConnection()),
this, SLOT(onNewConnection()));
@@ -146,7 +150,9 @@ private Q_SLOTS:
void tst_sendTextMessage();
void tst_sendBinaryMessage();
void tst_errorString();
+ void tst_openRequest_data();
void tst_openRequest();
+ void tst_protocolAccessor();
void tst_moveToThread();
void tst_moveToThreadNoWarning();
#ifndef QT_NO_NETWORKPROXY
@@ -631,8 +637,35 @@ void tst_QWebSocket::tst_errorString()
QCOMPARE(socket.errorString(), QStringLiteral("Host not found"));
}
+void tst_QWebSocket::tst_openRequest_data()
+{
+ QTest::addColumn<QStringList>("subprotocols");
+ QTest::addColumn<QString>("subprotocolHeader");
+ QTest::addColumn<QRegularExpression>("warningExpression");
+
+ QTest::addRow("no subprotocols") << QStringList{} << QString{} << QRegularExpression{};
+ QTest::addRow("single subprotocol") << QStringList{"foobar"} << QStringLiteral("foobar")
+ << QRegularExpression{};
+ QTest::addRow("multiple subprotocols") << QStringList{"foo", "bar"}
+ << QStringLiteral("foo, bar")
+ << QRegularExpression{};
+ QTest::addRow("subprotocol with whitespace")
+ << QStringList{"chat", "foo\r\nbar with space"}
+ << QStringLiteral("chat")
+ << QRegularExpression{".*invalid.*bar with space"};
+
+ QTest::addRow("subprotocol with invalid chars")
+ << QStringList{"chat", "foo{}"}
+ << QStringLiteral("chat")
+ << QRegularExpression{".*invalid.*foo"};
+}
+
void tst_QWebSocket::tst_openRequest()
{
+ QFETCH(QStringList, subprotocols);
+ QFETCH(QString, subprotocolHeader);
+ QFETCH(QRegularExpression, warningExpression);
+
EchoServer echoServer;
QWebSocket socket;
@@ -647,7 +680,13 @@ void tst_QWebSocket::tst_openRequest()
url.setQuery(query);
QNetworkRequest req(url);
req.setRawHeader("X-Custom-Header", "A custom header");
- socket.open(req);
+ QWebSocketHandshakeOptions options;
+ options.setSubprotocols(subprotocols);
+
+ if (!warningExpression.pattern().isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, warningExpression);
+
+ socket.open(req, options);
QTRY_COMPARE(socketConnectedSpy.count(), 1);
QTRY_COMPARE(serverRequestSpy.count(), 1);
@@ -656,6 +695,34 @@ void tst_QWebSocket::tst_openRequest()
QNetworkRequest requestConnected = arguments.at(0).value<QNetworkRequest>();
QCOMPARE(requestConnected.url(), req.url());
QCOMPARE(requestConnected.rawHeader("X-Custom-Header"), req.rawHeader("X-Custom-Header"));
+
+ if (subprotocols.isEmpty())
+ QVERIFY(!requestConnected.hasRawHeader("Sec-WebSocket-Protocol"));
+ else
+ QCOMPARE(requestConnected.rawHeader("Sec-WebSocket-Protocol"), subprotocolHeader);
+
+
+ socket.close();
+}
+
+void tst_QWebSocket::tst_protocolAccessor()
+{
+ EchoServer echoServer;
+
+ QWebSocket socket;
+
+ QUrl url = QUrl(QStringLiteral("ws://") + echoServer.hostAddress().toString() +
+ QLatin1Char(':') + QString::number(echoServer.port()));
+
+ QWebSocketHandshakeOptions options;
+ options.setSubprotocols({ "foo", "protocol2" });
+
+ socket.open(url, options);
+
+ QTRY_COMPARE(socket.state(), QAbstractSocket::ConnectedState);
+
+ QCOMPARE(socket.subprotocol(), "protocol2");
+
socket.close();
}
diff --git a/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp b/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
index b2dd95f..c25e864 100644
--- a/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
+++ b/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
@@ -37,6 +37,7 @@
#include <QtNetwork/qsslkey.h>
#include <QtNetwork/qsslsocket.h>
#endif
+#include <QtWebSockets/QWebSocketHandshakeOptions>
#include <QtWebSockets/QWebSocketServer>
#include <QtWebSockets/QWebSocket>
#include <QtWebSockets/QWebSocketCorsAuthenticator>
@@ -106,6 +107,8 @@ private Q_SLOTS:
void tst_settersAndGetters();
void tst_listening();
void tst_connectivity();
+ void tst_protocols_data();
+ void tst_protocols();
void tst_preSharedKey();
void tst_maxPendingConnections();
void tst_serverDestroyedWhileSocketConnected();
@@ -375,6 +378,48 @@ void tst_QWebSocketServer::tst_connectivity()
QCOMPARE(serverErrorSpy.count(), 0);
}
+void tst_QWebSocketServer::tst_protocols_data()
+{
+ QTest::addColumn<QStringList>("clientProtocols");
+ QTest::addColumn<QString>("expectedProtocol");
+ QTest::addRow("none") << QStringList {} << QString {};
+ QTest::addRow("same order as server")
+ << QStringList { "chat", "superchat" } << QStringLiteral("chat");
+ QTest::addRow("different order from server")
+ << QStringList { "superchat", "chat" } << QStringLiteral("superchat");
+ QTest::addRow("unsupported protocol") << QStringList { "foo" } << QString {};
+ QTest::addRow("mixed supported/unsupported protocol")
+ << QStringList { "foo", "chat" } << QStringLiteral("chat");
+}
+
+void tst_QWebSocketServer::tst_protocols()
+{
+ QFETCH(QStringList, clientProtocols);
+ QFETCH(QString, expectedProtocol);
+
+ QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
+ server.setSupportedSubprotocols({ QStringLiteral("chat"), QStringLiteral("superchat") });
+
+ QSignalSpy newConnectionSpy(&server, &QWebSocketServer::newConnection);
+
+ QVERIFY(server.listen());
+
+ QWebSocketHandshakeOptions opt;
+ opt.setSubprotocols(clientProtocols);
+ QWebSocket client;
+ client.open(server.serverUrl(), opt);
+
+ QTRY_COMPARE(client.state(), QAbstractSocket::ConnectedState);
+ QTRY_COMPARE(newConnectionSpy.count(), 1);
+
+ auto *serverSocket = server.nextPendingConnection();
+ QVERIFY(serverSocket);
+
+ QCOMPARE(client.subprotocol(), expectedProtocol);
+ QCOMPARE(serverSocket->subprotocol(), expectedProtocol);
+ QCOMPARE(serverSocket->handshakeOptions().subprotocols(), clientProtocols);
+}
+
void tst_QWebSocketServer::tst_preSharedKey()
{
if (m_shouldSkipUnsupportedIpv6Test)