1/****************************************************************************
2**
3** Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28#include <QString>
29#include <QtTest>
30#include <QNetworkProxy>
31#include <QTcpSocket>
32#include <QTcpServer>
33#include <QtCore/QScopedPointer>
34#ifndef QT_NO_OPENSSL
35#include <QtNetwork/qsslpresharedkeyauthenticator.h>
36#endif
37#ifndef QT_NO_SSL
38#include <QtNetwork/qsslcipher.h>
39#include <QtNetwork/qsslkey.h>
40#include <QtNetwork/qsslsocket.h>
41#endif
42#include <QtWebSockets/QWebSocketServer>
43#include <QtWebSockets/QWebSocket>
44#include <QtWebSockets/QWebSocketCorsAuthenticator>
45#include <QtWebSockets/qwebsocketprotocol.h>
46
47QT_USE_NAMESPACE
48
49Q_DECLARE_METATYPE(QWebSocketProtocol::Version)
50Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode)
51Q_DECLARE_METATYPE(QWebSocketServer::SslMode)
52Q_DECLARE_METATYPE(QWebSocketCorsAuthenticator *)
53#ifndef QT_NO_SSL
54Q_DECLARE_METATYPE(QSslError)
55#endif
56
57#ifndef QT_NO_OPENSSL
58// Use this cipher to force PSK key sharing.
59static const QString PSK_CIPHER_WITHOUT_AUTH = QStringLiteral("PSK-AES256-CBC-SHA");
60static const QByteArray PSK_CLIENT_PRESHAREDKEY = QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f");
61static const QByteArray PSK_SERVER_IDENTITY_HINT = QByteArrayLiteral("QtTestServerHint");
62static const QByteArray PSK_CLIENT_IDENTITY = QByteArrayLiteral("Client_identity");
63
64class PskProvider : public QObject
65{
66 Q_OBJECT
67
68public:
69 bool m_server = false;
70 QByteArray m_identity;
71 QByteArray m_psk;
72
73public slots:
74 void providePsk(QSslPreSharedKeyAuthenticator *authenticator)
75 {
76 QVERIFY(authenticator);
77 QCOMPARE(authenticator->identityHint(), PSK_SERVER_IDENTITY_HINT);
78 if (m_server)
79 QCOMPARE(authenticator->maximumIdentityLength(), 0);
80 else
81 QVERIFY(authenticator->maximumIdentityLength() > 0);
82
83 QVERIFY(authenticator->maximumPreSharedKeyLength() > 0);
84
85 if (!m_identity.isEmpty()) {
86 authenticator->setIdentity(m_identity);
87 QCOMPARE(authenticator->identity(), m_identity);
88 }
89
90 if (!m_psk.isEmpty()) {
91 authenticator->setPreSharedKey(m_psk);
92 QCOMPARE(authenticator->preSharedKey(), m_psk);
93 }
94 }
95};
96#endif
97
98class tst_QWebSocketServer : public QObject
99{
100 Q_OBJECT
101
102public:
103 tst_QWebSocketServer();
104
105private Q_SLOTS:
106 void init();
107 void initTestCase();
108 void cleanupTestCase();
109 void tst_initialisation();
110 void tst_settersAndGetters();
111 void tst_listening();
112 void tst_connectivity();
113 void tst_preSharedKey();
114 void tst_maxPendingConnections();
115 void tst_serverDestroyedWhileSocketConnected();
116 void tst_scheme(); // qtbug-55927
117 void tst_handleConnection();
118 void tst_handshakeTimeout(); // qtbug-63312, qtbug-57026
119 void multipleFrames();
120
121private:
122 bool m_shouldSkipUnsupportedIpv6Test;
123#ifdef SHOULD_CHECK_SYSCALL_SUPPORT
124 bool ipv6GetsockoptionMissing(int level, int optname);
125#endif
126};
127
128tst_QWebSocketServer::tst_QWebSocketServer() : m_shouldSkipUnsupportedIpv6Test(false)
129{
130}
131
132void tst_QWebSocketServer::init()
133{
134 qRegisterMetaType<QWebSocketProtocol::Version>(typeName: "QWebSocketProtocol::Version");
135 qRegisterMetaType<QWebSocketProtocol::CloseCode>(typeName: "QWebSocketProtocol::CloseCode");
136 qRegisterMetaType<QWebSocketServer::SslMode>(typeName: "QWebSocketServer::SslMode");
137 qRegisterMetaType<QWebSocketCorsAuthenticator *>(typeName: "QWebSocketCorsAuthenticator *");
138#ifndef QT_NO_SSL
139 qRegisterMetaType<QSslError>(typeName: "QSslError");
140#ifndef QT_NO_OPENSSL
141 qRegisterMetaType<QSslPreSharedKeyAuthenticator *>();
142#endif
143#endif
144}
145
146#ifdef SHOULD_CHECK_SYSCALL_SUPPORT
147#include <netinet/in.h>
148#include <sys/socket.h>
149#include <errno.h>
150#include <unistd.h>
151
152bool tst_QWebSocketServer::ipv6GetsockoptionMissing(int level, int optname)
153{
154 int testSocket;
155
156 testSocket = socket(PF_INET6, SOCK_STREAM, 0);
157
158 // If we can't test here, assume it's not missing
159 if (testSocket == -1)
160 return false;
161
162 bool result = false;
163 if (getsockopt(testSocket, level, optname, nullptr, 0) == -1) {
164 if (errno == EOPNOTSUPP) {
165 result = true;
166 }
167 }
168
169 close(testSocket);
170 return result;
171}
172
173#endif //SHOULD_CHECK_SYSCALL_SUPPORT
174
175void tst_QWebSocketServer::initTestCase()
176{
177#ifdef SHOULD_CHECK_SYSCALL_SUPPORT
178 // Qemu does not have required support for IPV6 socket options.
179 // If this is detected, skip the test
180 m_shouldSkipUnsupportedIpv6Test = ipv6GetsockoptionMissing(SOL_IPV6, IPV6_V6ONLY);
181#endif
182}
183
184void tst_QWebSocketServer::cleanupTestCase()
185{
186}
187
188void tst_QWebSocketServer::tst_initialisation()
189{
190 {
191 QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
192
193 QVERIFY(server.serverName().isEmpty());
194 QCOMPARE(server.secureMode(), QWebSocketServer::NonSecureMode);
195 QVERIFY(!server.isListening());
196 QCOMPARE(server.maxPendingConnections(), 30);
197 QCOMPARE(server.serverPort(), quint16(0));
198 QCOMPARE(server.serverAddress(), QHostAddress());
199#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
200 QCOMPARE(server.socketDescriptor(), -1);
201#else // ### Qt 6: Remove leftovers
202 QCOMPARE(server.nativeDescriptor(), -1);
203#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
204 QVERIFY(!server.hasPendingConnections());
205 QVERIFY(!server.nextPendingConnection());
206 QCOMPARE(server.error(), QWebSocketProtocol::CloseCodeNormal);
207 QVERIFY(server.errorString().isEmpty());
208 #ifndef QT_NO_NETWORKPROXY
209 QCOMPARE(server.proxy().type(), QNetworkProxy::DefaultProxy);
210 #endif
211 #ifndef QT_NO_SSL
212 QCOMPARE(server.sslConfiguration(), QSslConfiguration::defaultConfiguration());
213 #endif
214 QCOMPARE(server.supportedVersions().count(), 1);
215 QCOMPARE(server.supportedVersions().at(0), QWebSocketProtocol::VersionLatest);
216 QCOMPARE(server.supportedVersions().at(0), QWebSocketProtocol::Version13);
217
218 server.close();
219 //closing a server should not affect any of the parameters
220 //certainly if the server was not opened before
221
222 QVERIFY(server.serverName().isEmpty());
223 QCOMPARE(server.secureMode(), QWebSocketServer::NonSecureMode);
224 QVERIFY(!server.isListening());
225 QCOMPARE(server.maxPendingConnections(), 30);
226 QCOMPARE(server.serverPort(), quint16(0));
227 QCOMPARE(server.serverAddress(), QHostAddress());
228#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
229 QCOMPARE(server.socketDescriptor(), -1);
230#else // ### Qt 6: Remove leftovers
231 QCOMPARE(server.nativeDescriptor(), -1);
232#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
233 QVERIFY(!server.hasPendingConnections());
234 QVERIFY(!server.nextPendingConnection());
235 QCOMPARE(server.error(), QWebSocketProtocol::CloseCodeNormal);
236 QVERIFY(server.errorString().isEmpty());
237 #ifndef QT_NO_NETWORKPROXY
238 QCOMPARE(server.proxy().type(), QNetworkProxy::DefaultProxy);
239 #endif
240 #ifndef QT_NO_SSL
241 QCOMPARE(server.sslConfiguration(), QSslConfiguration::defaultConfiguration());
242 #endif
243 QCOMPARE(server.supportedVersions().count(), 1);
244 QCOMPARE(server.supportedVersions().at(0), QWebSocketProtocol::VersionLatest);
245 QCOMPARE(server.supportedVersions().at(0), QWebSocketProtocol::Version13);
246 QCOMPARE(server.serverUrl(), QUrl());
247 }
248
249 {
250#ifndef QT_NO_SSL
251 QWebSocketServer sslServer(QString(), QWebSocketServer::SecureMode);
252 QCOMPARE(sslServer.secureMode(), QWebSocketServer::SecureMode);
253#endif
254 }
255}
256
257void tst_QWebSocketServer::tst_settersAndGetters()
258{
259 QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
260
261 server.setMaxPendingConnections(23);
262 QCOMPARE(server.maxPendingConnections(), 23);
263 server.setMaxPendingConnections(INT_MIN);
264 QCOMPARE(server.maxPendingConnections(), INT_MIN);
265 server.setMaxPendingConnections(INT_MAX);
266 QCOMPARE(server.maxPendingConnections(), INT_MAX);
267
268#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
269 QVERIFY(!server.setSocketDescriptor(-2));
270 QCOMPARE(server.socketDescriptor(), -1);
271#else // ### Qt 6: Remove leftovers
272 QVERIFY(!server.setNativeDescriptor(-2));
273 QCOMPARE(server.nativeDescriptor(), -1);
274#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
275
276 server.setServerName(QStringLiteral("Qt WebSocketServer"));
277 QCOMPARE(server.serverName(), QStringLiteral("Qt WebSocketServer"));
278
279#ifndef QT_NO_NETWORKPROXY
280 QNetworkProxy proxy(QNetworkProxy::Socks5Proxy);
281 server.setProxy(proxy);
282 QCOMPARE(server.proxy(), proxy);
283#endif
284#ifndef QT_NO_SSL
285 //cannot set an ssl configuration on a non secure server
286 QSslConfiguration sslConfiguration = QSslConfiguration::defaultConfiguration();
287 sslConfiguration.setPeerVerifyDepth(sslConfiguration.peerVerifyDepth() + 1);
288 server.setSslConfiguration(sslConfiguration);
289 QVERIFY(server.sslConfiguration() != sslConfiguration);
290 QCOMPARE(server.sslConfiguration(), QSslConfiguration::defaultConfiguration());
291
292 QWebSocketServer sslServer(QString(), QWebSocketServer::SecureMode);
293 sslServer.setSslConfiguration(sslConfiguration);
294 QCOMPARE(sslServer.sslConfiguration(), sslConfiguration);
295 QVERIFY(sslServer.sslConfiguration() != QSslConfiguration::defaultConfiguration());
296#endif
297
298 server.setHandshakeTimeout(64);
299 QCOMPARE(server.handshakeTimeoutMS(), 64);
300#if QT_HAS_INCLUDE(<chrono>)
301 auto expected = std::chrono::milliseconds(64);
302 QCOMPARE(server.handshakeTimeout(), expected);
303
304 expected = std::chrono::milliseconds(242);
305 server.setHandshakeTimeout(expected);
306 QCOMPARE(server.handshakeTimeoutMS(), 242);
307 QCOMPARE(server.handshakeTimeout(), expected);
308#endif
309}
310
311void tst_QWebSocketServer::tst_listening()
312{
313 //These listening tests are not too extensive, as the implementation of QWebSocketServer
314 //relies on QTcpServer
315
316 QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
317
318 QSignalSpy serverAcceptErrorSpy(&server, SIGNAL(acceptError(QAbstractSocket::SocketError)));
319 QSignalSpy serverConnectionSpy(&server, SIGNAL(newConnection()));
320 QSignalSpy serverErrorSpy(&server,
321 SIGNAL(serverError(QWebSocketProtocol::CloseCode)));
322 QSignalSpy corsAuthenticationSpy(&server,
323 SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)));
324 QSignalSpy serverClosedSpy(&server, SIGNAL(closed()));
325#ifndef QT_NO_SSL
326 QSignalSpy peerVerifyErrorSpy(&server, SIGNAL(peerVerifyError(QSslError)));
327 QSignalSpy sslErrorsSpy(&server, SIGNAL(sslErrors(QList<QSslError>)));
328#endif
329
330 QVERIFY(server.listen()); //listen on all network interface, choose an appropriate port
331 QVERIFY(server.isListening());
332 QCOMPARE(serverClosedSpy.count(), 0);
333 server.close();
334 QTRY_COMPARE(serverClosedSpy.count(), 1);
335 QVERIFY(!server.isListening());
336 QCOMPARE(serverErrorSpy.count(), 0);
337
338 QVERIFY(!server.listen(QHostAddress(QStringLiteral("1.2.3.4")), 0));
339 QCOMPARE(server.error(), QWebSocketProtocol::CloseCodeAbnormalDisconnection);
340 QCOMPARE(server.errorString().toLatin1().constData(), "The address is not available");
341 QVERIFY(!server.isListening());
342
343 QCOMPARE(serverAcceptErrorSpy.count(), 0);
344 QCOMPARE(serverConnectionSpy.count(), 0);
345 QCOMPARE(corsAuthenticationSpy.count(), 0);
346#ifndef QT_NO_SSL
347 QCOMPARE(peerVerifyErrorSpy.count(), 0);
348 QCOMPARE(sslErrorsSpy.count(), 0);
349#endif
350 QCOMPARE(serverErrorSpy.count(), 1);
351 QCOMPARE(serverClosedSpy.count(), 1);
352}
353
354void tst_QWebSocketServer::tst_connectivity()
355{
356 if (m_shouldSkipUnsupportedIpv6Test)
357 QSKIP("Syscalls needed for ipv6 sockoptions missing functionality");
358
359 QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
360 QSignalSpy serverConnectionSpy(&server, SIGNAL(newConnection()));
361 QSignalSpy serverErrorSpy(&server,
362 SIGNAL(serverError(QWebSocketProtocol::CloseCode)));
363 QSignalSpy corsAuthenticationSpy(&server,
364 SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)));
365 QSignalSpy serverClosedSpy(&server, SIGNAL(closed()));
366#ifndef QT_NO_SSL
367 QSignalSpy peerVerifyErrorSpy(&server, SIGNAL(peerVerifyError(QSslError)));
368 QSignalSpy sslErrorsSpy(&server, SIGNAL(sslErrors(QList<QSslError>)));
369#endif
370 QWebSocket socket;
371 QSignalSpy socketConnectedSpy(&socket, SIGNAL(connected()));
372
373 QVERIFY(server.listen());
374 QCOMPARE(server.serverAddress(), QHostAddress(QHostAddress::Any));
375 QCOMPARE(server.serverUrl(), QUrl(QStringLiteral("ws://") + QHostAddress(QHostAddress::LocalHost).toString() +
376 QStringLiteral(":").append(QString::number(server.serverPort()))));
377
378 socket.open(url: server.serverUrl().toString());
379
380 QTRY_COMPARE(socketConnectedSpy.count(), 1);
381 QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
382 QCOMPARE(serverConnectionSpy.count(), 1);
383 QCOMPARE(corsAuthenticationSpy.count(), 1);
384
385 QCOMPARE(serverClosedSpy.count(), 0);
386
387 server.close();
388
389 QTRY_COMPARE(serverClosedSpy.count(), 1);
390#ifndef QT_NO_SSL
391 QCOMPARE(peerVerifyErrorSpy.count(), 0);
392 QCOMPARE(sslErrorsSpy.count(), 0);
393#endif
394 QCOMPARE(serverErrorSpy.count(), 0);
395}
396
397void tst_QWebSocketServer::tst_preSharedKey()
398{
399 if (m_shouldSkipUnsupportedIpv6Test)
400 QSKIP("Syscalls needed for ipv6 sockoptions missing functionality");
401
402#ifndef QT_NO_OPENSSL
403 QWebSocketServer server(QString(), QWebSocketServer::SecureMode);
404
405 bool cipherFound = false;
406 const QList<QSslCipher> supportedCiphers = QSslConfiguration::supportedCiphers();
407 for (const QSslCipher &cipher : supportedCiphers) {
408 if (cipher.name() == PSK_CIPHER_WITHOUT_AUTH) {
409 cipherFound = true;
410 break;
411 }
412 }
413
414 if (!cipherFound)
415 QSKIP("SSL implementation does not support the necessary cipher");
416
417 QSslCipher cipher(PSK_CIPHER_WITHOUT_AUTH);
418 QList<QSslCipher> list;
419 list << cipher;
420
421 QSslConfiguration config = QSslConfiguration::defaultConfiguration();
422 if (QSslSocket::sslLibraryVersionNumber() >= 0x10101000L)
423 config.setProtocol(QSsl::TlsV1_2); // With TLS 1.3 there are some issues with PSK, force 1.2
424 config.setCiphers(list);
425 config.setPeerVerifyMode(QSslSocket::VerifyNone);
426 config.setPreSharedKeyIdentityHint(PSK_SERVER_IDENTITY_HINT);
427 server.setSslConfiguration(config);
428
429 PskProvider providerServer;
430 providerServer.m_server = true;
431 providerServer.m_identity = PSK_CLIENT_IDENTITY;
432 providerServer.m_psk = PSK_CLIENT_PRESHAREDKEY;
433 connect(sender: &server, signal: &QWebSocketServer::preSharedKeyAuthenticationRequired, receiver: &providerServer, slot: &PskProvider::providePsk);
434
435 QSignalSpy serverPskRequiredSpy(&server, &QWebSocketServer::preSharedKeyAuthenticationRequired);
436 QSignalSpy serverConnectionSpy(&server, &QWebSocketServer::newConnection);
437 QSignalSpy serverErrorSpy(&server,
438 SIGNAL(serverError(QWebSocketProtocol::CloseCode)));
439 QSignalSpy serverClosedSpy(&server, &QWebSocketServer::closed);
440 QSignalSpy sslErrorsSpy(&server, SIGNAL(sslErrors(QList<QSslError>)));
441
442 QWebSocket socket;
443 QSslConfiguration socketConfig = QSslConfiguration::defaultConfiguration();
444 if (QSslSocket::sslLibraryVersionNumber() >= 0x10101000L)
445 socketConfig.setProtocol(QSsl::TlsV1_2); // With TLS 1.3 there are some issues with PSK, force 1.2
446 socketConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
447 socketConfig.setCiphers(list);
448 socket.setSslConfiguration(socketConfig);
449
450 PskProvider providerClient;
451 providerClient.m_identity = PSK_CLIENT_IDENTITY;
452 providerClient.m_psk = PSK_CLIENT_PRESHAREDKEY;
453 connect(sender: &socket, signal: &QWebSocket::preSharedKeyAuthenticationRequired, receiver: &providerClient, slot: &PskProvider::providePsk);
454 QSignalSpy socketPskRequiredSpy(&socket, &QWebSocket::preSharedKeyAuthenticationRequired);
455 QSignalSpy socketConnectedSpy(&socket, &QWebSocket::connected);
456
457 QVERIFY(server.listen());
458 QCOMPARE(server.serverAddress(), QHostAddress(QHostAddress::Any));
459 QCOMPARE(server.serverUrl(), QUrl(QString::asprintf("wss://%ls:%d",
460 qUtf16Printable(QHostAddress(QHostAddress::LocalHost).toString()), server.serverPort())));
461
462 socket.open(url: server.serverUrl().toString());
463
464 QTRY_COMPARE(socketConnectedSpy.count(), 1);
465 QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
466 QCOMPARE(serverConnectionSpy.count(), 1);
467 QCOMPARE(serverPskRequiredSpy.count(), 1);
468 QCOMPARE(socketPskRequiredSpy.count(), 1);
469
470 QCOMPARE(serverClosedSpy.count(), 0);
471
472 server.close();
473
474 QTRY_COMPARE(serverClosedSpy.count(), 1);
475 QCOMPARE(sslErrorsSpy.count(), 0);
476 QCOMPARE(serverErrorSpy.count(), 0);
477#endif
478}
479
480void tst_QWebSocketServer::tst_maxPendingConnections()
481{
482 if (m_shouldSkipUnsupportedIpv6Test)
483 QSKIP("Syscalls needed for ipv6 sockoptions missing functionality");
484
485 //tests if maximum connections are respected
486 //also checks if there are no side-effects like signals that are unexpectedly thrown
487 QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
488 server.setMaxPendingConnections(2);
489 QSignalSpy serverConnectionSpy(&server, SIGNAL(newConnection()));
490 QSignalSpy serverErrorSpy(&server,
491 SIGNAL(serverError(QWebSocketProtocol::CloseCode)));
492 QSignalSpy corsAuthenticationSpy(&server,
493 SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)));
494 QSignalSpy serverClosedSpy(&server, SIGNAL(closed()));
495#ifndef QT_NO_SSL
496 QSignalSpy peerVerifyErrorSpy(&server, SIGNAL(peerVerifyError(QSslError)));
497 QSignalSpy sslErrorsSpy(&server, SIGNAL(sslErrors(QList<QSslError>)));
498#endif
499 QSignalSpy serverAcceptErrorSpy(&server, SIGNAL(acceptError(QAbstractSocket::SocketError)));
500
501 QWebSocket socket1;
502 QWebSocket socket2;
503 QWebSocket socket3;
504
505 QSignalSpy socket1ConnectedSpy(&socket1, SIGNAL(connected()));
506 QSignalSpy socket2ConnectedSpy(&socket2, SIGNAL(connected()));
507 QSignalSpy socket3ConnectedSpy(&socket3, SIGNAL(connected()));
508
509 QVERIFY(server.listen());
510
511 socket1.open(url: server.serverUrl().toString());
512
513 QTRY_COMPARE(socket1ConnectedSpy.count(), 1);
514 QCOMPARE(socket1.state(), QAbstractSocket::ConnectedState);
515 QCOMPARE(serverConnectionSpy.count(), 1);
516 QCOMPARE(corsAuthenticationSpy.count(), 1);
517 socket2.open(url: server.serverUrl().toString());
518 QTRY_COMPARE(socket2ConnectedSpy.count(), 1);
519 QCOMPARE(socket2.state(), QAbstractSocket::ConnectedState);
520 QCOMPARE(serverConnectionSpy.count(), 2);
521 QCOMPARE(corsAuthenticationSpy.count(), 2);
522 socket3.open(url: server.serverUrl().toString());
523 QVERIFY(!socket3ConnectedSpy.wait(250));
524 QCOMPARE(socket3ConnectedSpy.count(), 0);
525 QCOMPARE(socket3.state(), QAbstractSocket::UnconnectedState);
526 QCOMPARE(serverConnectionSpy.count(), 2);
527 QCOMPARE(corsAuthenticationSpy.count(), 2);
528
529 QVERIFY(server.hasPendingConnections());
530 QWebSocket *pSocket = server.nextPendingConnection();
531 QVERIFY(pSocket);
532 delete pSocket;
533 QVERIFY(server.hasPendingConnections());
534 pSocket = server.nextPendingConnection();
535 QVERIFY(pSocket);
536 delete pSocket;
537 QVERIFY(!server.hasPendingConnections());
538 QVERIFY(!server.nextPendingConnection());
539
540//will resolve in another commit
541#ifndef Q_OS_WIN
542 QCOMPARE(serverErrorSpy.count(), 1);
543 QCOMPARE(serverErrorSpy.at(0).at(0).value<QWebSocketProtocol::CloseCode>(),
544 QWebSocketProtocol::CloseCodeAbnormalDisconnection);
545#endif
546 QCOMPARE(serverClosedSpy.count(), 0);
547
548 server.close();
549
550 QTRY_COMPARE(serverClosedSpy.count(), 1);
551#ifndef QT_NO_SSL
552 QCOMPARE(peerVerifyErrorSpy.count(), 0);
553 QCOMPARE(sslErrorsSpy.count(), 0);
554#endif
555 QCOMPARE(serverAcceptErrorSpy.count(), 0);
556}
557
558void tst_QWebSocketServer::tst_serverDestroyedWhileSocketConnected()
559{
560 if (m_shouldSkipUnsupportedIpv6Test)
561 QSKIP("Syscalls needed for ipv6 sockoptions missing functionality");
562
563 QWebSocketServer * server = new QWebSocketServer(QString(), QWebSocketServer::NonSecureMode);
564 QSignalSpy serverConnectionSpy(server, SIGNAL(newConnection()));
565 QSignalSpy corsAuthenticationSpy(server,
566 SIGNAL(originAuthenticationRequired(QWebSocketCorsAuthenticator*)));
567 QSignalSpy serverClosedSpy(server, SIGNAL(closed()));
568
569 QWebSocket socket;
570 QSignalSpy socketConnectedSpy(&socket, SIGNAL(connected()));
571 QSignalSpy socketDisconnectedSpy(&socket, SIGNAL(disconnected()));
572
573 QVERIFY(server->listen());
574 QCOMPARE(server->serverAddress(), QHostAddress(QHostAddress::Any));
575 QCOMPARE(server->serverUrl(), QUrl(QStringLiteral("ws://") + QHostAddress(QHostAddress::LocalHost).toString() +
576 QStringLiteral(":").append(QString::number(server->serverPort()))));
577
578 socket.open(url: server->serverUrl().toString());
579
580 QTRY_COMPARE(socketConnectedSpy.count(), 1);
581 QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
582 QCOMPARE(serverConnectionSpy.count(), 1);
583 QCOMPARE(corsAuthenticationSpy.count(), 1);
584
585 QCOMPARE(serverClosedSpy.count(), 0);
586
587 delete server;
588
589 QTRY_COMPARE(socketDisconnectedSpy.count(), 1);
590}
591
592#ifndef QT_NO_SSL
593static void setupSecureServer(QWebSocketServer *secureServer)
594{
595 QSslConfiguration sslConfiguration;
596 QFile certFile(QStringLiteral(":/localhost.cert"));
597 QFile keyFile(QStringLiteral(":/localhost.key"));
598 QVERIFY(certFile.open(QIODevice::ReadOnly));
599 QVERIFY(keyFile.open(QIODevice::ReadOnly));
600 QSslCertificate certificate(&certFile, QSsl::Pem);
601 QSslKey sslKey(&keyFile, QSsl::Rsa, QSsl::Pem);
602 certFile.close();
603 keyFile.close();
604 sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
605 sslConfiguration.setLocalCertificate(certificate);
606 sslConfiguration.setPrivateKey(sslKey);
607 secureServer->setSslConfiguration(sslConfiguration);
608}
609#endif
610
611void tst_QWebSocketServer::tst_scheme()
612{
613 if (m_shouldSkipUnsupportedIpv6Test)
614 QSKIP("Syscalls needed for ipv6 sockoptions missing functionality");
615
616 QWebSocketServer plainServer(QString(), QWebSocketServer::NonSecureMode);
617 QSignalSpy plainServerConnectionSpy(&plainServer, SIGNAL(newConnection()));
618
619 QVERIFY(plainServer.listen());
620
621 QWebSocket plainSocket;
622 plainSocket.open(url: plainServer.serverUrl().toString());
623
624 QTRY_COMPARE(plainServerConnectionSpy.count(), 1);
625 QScopedPointer<QWebSocket> plainServerSocket(plainServer.nextPendingConnection());
626 QVERIFY(!plainServerSocket.isNull());
627 QCOMPARE(plainServerSocket->requestUrl().scheme(), QStringLiteral("ws"));
628 plainServer.close();
629
630#ifndef QT_NO_SSL
631 QWebSocketServer secureServer(QString(), QWebSocketServer::SecureMode);
632 setupSecureServer(&secureServer);
633 if (QTest::currentTestFailed())
634 return;
635 QSignalSpy secureServerConnectionSpy(&secureServer, SIGNAL(newConnection()));
636
637 QVERIFY(secureServer.listen());
638
639 QSslCipher sessionCipher;
640 QWebSocket secureSocket;
641 connect(sender: &secureSocket, signal: &QWebSocket::sslErrors,
642 context: &secureSocket, slot: [&] {
643 secureSocket.ignoreSslErrors();
644 sessionCipher = secureSocket.sslConfiguration().sessionCipher();
645 });
646 secureSocket.open(url: secureServer.serverUrl().toString());
647
648 QTRY_COMPARE(secureServerConnectionSpy.count(), 1);
649 QScopedPointer<QWebSocket> secureServerSocket(secureServer.nextPendingConnection());
650 QVERIFY(!secureServerSocket.isNull());
651 QCOMPARE(secureServerSocket->requestUrl().scheme(), QStringLiteral("wss"));
652 secureServer.close();
653 QVERIFY(!sessionCipher.isNull());
654#endif
655}
656
657void tst_QWebSocketServer::tst_handleConnection()
658{
659 QWebSocketServer wsServer(QString(), QWebSocketServer::NonSecureMode);
660 QSignalSpy wsServerConnectionSpy(&wsServer, &QWebSocketServer::newConnection);
661
662 QTcpServer tcpServer;
663 connect(sender: &tcpServer, signal: &QTcpServer::newConnection,
664 slot: [&tcpServer, &wsServer]() {
665 wsServer.handleConnection(socket: tcpServer.nextPendingConnection());
666 });
667 QVERIFY(tcpServer.listen());
668
669 QWebSocket webSocket;
670 QSignalSpy wsConnectedSpy(&webSocket, &QWebSocket::connected);
671 webSocket.open(QStringLiteral("ws://localhost:%1").arg(a: tcpServer.serverPort()));
672 QTRY_COMPARE(wsConnectedSpy.count(), 1);
673
674 QTRY_COMPARE(wsServerConnectionSpy.count(), 1);
675
676 QScopedPointer<QWebSocket> webServerSocket(wsServer.nextPendingConnection());
677 QVERIFY(!webServerSocket.isNull());
678
679 QSignalSpy wsMessageReceivedSpy(webServerSocket.data(), &QWebSocket::textMessageReceived);
680 webSocket.sendTextMessage(message: "dummy");
681
682 QTRY_COMPARE(wsMessageReceivedSpy.count(), 1);
683 QList<QVariant> arguments = wsMessageReceivedSpy.takeFirst();
684 QCOMPARE(arguments.first().toString(), QString("dummy"));
685
686 QSignalSpy clientMessageReceivedSpy(&webSocket, &QWebSocket::textMessageReceived);
687 webServerSocket->sendTextMessage(message: "hello");
688 QVERIFY(webServerSocket->bytesToWrite() > 5); // 5 + a few extra bytes for header
689 QTRY_COMPARE(webServerSocket->bytesToWrite(), 0);
690 QTRY_COMPARE(clientMessageReceivedSpy.count(), 1);
691 arguments = clientMessageReceivedSpy.takeFirst();
692 QCOMPARE(arguments.first().toString(), QString("hello"));
693}
694
695struct SocketSpy {
696 QTcpSocket *socket;
697 QSignalSpy *disconnectSpy;
698 ~SocketSpy() {
699 delete socket;
700 delete disconnectSpy;
701 }
702};
703
704static void openManyConnections(QList<SocketSpy *> *sockets, quint16 port, int numConnections)
705{
706 for (int i = 0; i < numConnections; i++) {
707 QTcpSocket *c = new QTcpSocket;
708 QSignalSpy *spy = new QSignalSpy(c, &QTcpSocket::disconnected);
709
710 c->connectToHost(hostName: "127.0.0.1", port);
711
712 sockets->append(t: new SocketSpy{.socket: c, .disconnectSpy: spy});
713 }
714}
715
716// Sum the counts together, for better output on failure (e.g. "FAIL: Actual: 49, Expected: 50")
717static int sumSocketSpyCount(const QList<SocketSpy *> &sockets)
718{
719 return std::accumulate(first: sockets.cbegin(), last: sockets.cend(), init: 0, binary_op: [](int c, SocketSpy *s) {
720 return c + s->disconnectSpy->count();
721 });
722}
723
724void tst_QWebSocketServer::tst_handshakeTimeout()
725{
726 { // No Timeout
727 QWebSocketServer plainServer(QString(), QWebSocketServer::NonSecureMode);
728 plainServer.setHandshakeTimeout(-1);
729 QSignalSpy plainServerConnectionSpy(&plainServer, SIGNAL(newConnection()));
730
731 QVERIFY(plainServer.listen());
732
733 QWebSocket socket;
734 socket.open(url: plainServer.serverUrl().toString());
735
736 QTRY_COMPARE(plainServerConnectionSpy.count(), 1);
737 QScopedPointer<QWebSocket> plainServerSocket(plainServer.nextPendingConnection());
738 QVERIFY(!plainServerSocket.isNull());
739
740 plainServer.close();
741 }
742
743 { // Unencrypted
744 QWebSocketServer plainServer(QString(), QWebSocketServer::NonSecureMode);
745 plainServer.setHandshakeTimeout(500);
746 QSignalSpy plainServerConnectionSpy(&plainServer, SIGNAL(newConnection()));
747
748 QVERIFY(plainServer.listen());
749
750 /* QTcpServer has a default of 30 pending connections. The test checks
751 * whether, when that list is full, the connections are dropped after
752 * a timeout and later pending connections are processed. */
753 const int numConnections = 50;
754 QList<SocketSpy *> sockets;
755 auto cleaner = qScopeGuard(f: [&sockets]() { qDeleteAll(c: sockets); });
756 openManyConnections(sockets: &sockets, port: plainServer.serverPort(), numConnections);
757
758 QCoreApplication::processEvents();
759
760 /* We have 50 plain TCP connections open, that are not proper websockets. */
761 QCOMPARE(plainServerConnectionSpy.count(), 0);
762
763 QWebSocket socket;
764 socket.open(url: plainServer.serverUrl().toString());
765
766 /* Check that a real websocket will be processed after some non-websocket
767 * TCP connections timeout. */
768 QTRY_COMPARE(plainServerConnectionSpy.count(), 1);
769 QScopedPointer<QWebSocket> plainServerSocket(plainServer.nextPendingConnection());
770 QVERIFY(!plainServerSocket.isNull());
771
772 /* Check that all non websocket connections eventually timeout. */
773 QTRY_COMPARE(sumSocketSpyCount(sockets), numConnections);
774
775 plainServer.close();
776 }
777
778#if QT_CONFIG(ssl)
779 { // Encrypted
780 QWebSocketServer secureServer(QString(), QWebSocketServer::SecureMode);
781 setupSecureServer(&secureServer);
782 if (QTest::currentTestFailed())
783 return;
784 secureServer.setHandshakeTimeout(2000);
785
786 QSignalSpy secureServerConnectionSpy(&secureServer, SIGNAL(newConnection()));
787
788 QVERIFY(secureServer.listen());
789
790 const int numConnections = 50;
791 QList<SocketSpy *> sockets;
792 auto cleaner = qScopeGuard(f: [&sockets]() { qDeleteAll(c: sockets); });
793 openManyConnections(sockets: &sockets, port: secureServer.serverPort(), numConnections);
794
795 QCoreApplication::processEvents();
796 QCOMPARE(secureServerConnectionSpy.count(), 0);
797
798 QWebSocket secureSocket;
799 connect(sender: &secureSocket, signal: QOverload<QAbstractSocket::SocketError>::of(ptr: &QWebSocket::error),
800 slot: [](QAbstractSocket::SocketError error) {
801 // This shouldn't print but it's useful for debugging when/if it does.
802 qDebug() << "Error occurred in the client:" << error;
803 });
804 QSslConfiguration config = secureSocket.sslConfiguration();
805 config.setPeerVerifyMode(QSslSocket::VerifyNone);
806 secureSocket.setSslConfiguration(config);
807
808 secureSocket.open(url: secureServer.serverUrl().toString());
809
810 QTRY_COMPARE(secureServerConnectionSpy.count(), 1);
811 QScopedPointer<QWebSocket> serverSocket(secureServer.nextPendingConnection());
812 QVERIFY(!serverSocket.isNull());
813
814 QTRY_COMPARE(sumSocketSpyCount(sockets), numConnections);
815
816 secureServer.close();
817 }
818#endif
819
820 { // Ensure properly handshaked connections are not timed out
821 QWebSocketServer plainServer(QString(), QWebSocketServer::NonSecureMode);
822 plainServer.setHandshakeTimeout(250);
823 QSignalSpy plainServerConnectionSpy(&plainServer, SIGNAL(newConnection()));
824
825 QVERIFY(plainServer.listen());
826
827 QWebSocket socket;
828 QSignalSpy socketConnectedSpy(&socket, &QWebSocket::connected);
829 QSignalSpy socketDisconnectedSpy(&socket, &QWebSocket::disconnected);
830 socket.open(url: plainServer.serverUrl().toString());
831
832 QTRY_COMPARE(plainServerConnectionSpy.count(), 1);
833 QTRY_COMPARE(socketConnectedSpy.count(), 1);
834
835 QEventLoop loop;
836 QTimer::singleShot(interval: 500, receiver: &loop, slot: &QEventLoop::quit);
837 loop.exec();
838
839 QCOMPARE(socketDisconnectedSpy.count(), 0);
840 }
841}
842
843void tst_QWebSocketServer::multipleFrames()
844{
845 QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode);
846 QSignalSpy serverConnectionSpy(&server, &QWebSocketServer::newConnection);
847 QVERIFY(server.listen());
848
849 QWebSocket socket;
850 QSignalSpy socketConnectedSpy(&socket, &QWebSocket::connected);
851 QSignalSpy messageReceivedSpy(&socket, &QWebSocket::binaryMessageReceived);
852 socket.open(url: server.serverUrl().toString());
853
854 QVERIFY(serverConnectionSpy.wait());
855 QVERIFY(socketConnectedSpy.wait());
856
857 auto serverSocket = std::unique_ptr<QWebSocket>(server.nextPendingConnection());
858 QVERIFY(serverSocket);
859 for (int i = 0; i < 10; i++)
860 serverSocket->sendBinaryMessage(data: QByteArray("abc"));
861 if (serverSocket->bytesToWrite())
862 QVERIFY(serverSocket->flush());
863
864 QVERIFY(messageReceivedSpy.wait());
865 // Since there's no guarantee the operating system will fit all 10 websocket frames into 1 tcp
866 // frame, let's just assume it will do at least 2. EXCEPT_FAIL any which doesn't merge any.
867 QVERIFY2(messageReceivedSpy.count() > 1, "Received only 1 message in the TCP frame!");
868}
869
870QTEST_MAIN(tst_QWebSocketServer)
871
872#include "tst_qwebsocketserver.moc"
873

source code of qtwebsockets/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp