2022-06-22 12:54:22 +00:00
|
|
|
// Copyright (C) 2019 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
2018-03-08 17:45:53 +00:00
|
|
|
|
|
|
|
#include <QtHttpServer/qabstracthttpserver.h>
|
|
|
|
|
|
|
|
#if defined(QT_WEBSOCKETS_LIB)
|
2020-04-09 09:18:07 +00:00
|
|
|
# include <QtWebSockets/qwebsocket.h>
|
2018-03-08 17:45:53 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <QtTest/qsignalspy.h>
|
|
|
|
#include <QtTest/qtest.h>
|
2024-04-30 11:05:29 +00:00
|
|
|
#include <QtTest/qtesteventloop.h>
|
2018-03-08 17:45:53 +00:00
|
|
|
|
|
|
|
#include <QtCore/qregularexpression.h>
|
|
|
|
#include <QtCore/qurl.h>
|
2024-04-30 11:05:29 +00:00
|
|
|
#include <QtHttpServer/qhttpserverrequest.h>
|
|
|
|
#include <QtHttpServer/qhttpserverresponder.h>
|
2024-04-22 08:32:53 +00:00
|
|
|
#include <QtHttpServer/qhttpserverwebsocketupgraderesponse.h>
|
2018-03-08 17:45:53 +00:00
|
|
|
#include <QtNetwork/qnetworkaccessmanager.h>
|
|
|
|
#include <QtNetwork/qnetworkreply.h>
|
|
|
|
#include <QtNetwork/qnetworkrequest.h>
|
|
|
|
#include <QtNetwork/qtcpserver.h>
|
2023-01-20 13:03:30 +00:00
|
|
|
#include <QtNetwork/qtcpsocket.h>
|
2024-04-30 11:05:29 +00:00
|
|
|
|
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
#ifdef QT_BUILD_INTERNAL
|
|
|
|
#include <QtNetwork/private/http2frames_p.h>
|
|
|
|
#include <QtNetwork/private/http2protocol_p.h>
|
|
|
|
#endif
|
|
|
|
#include <QtNetwork/qhttp2configuration.h>
|
|
|
|
#include <QtNetwork/qsslconfiguration.h>
|
|
|
|
#include <QtNetwork/qsslkey.h>
|
2024-04-22 08:32:53 +00:00
|
|
|
#include <QtNetwork/qsslserver.h>
|
2024-06-12 09:56:04 +00:00
|
|
|
#include <QtNetwork/private/qhttp2connection_p.h>
|
2024-04-30 11:05:29 +00:00
|
|
|
#endif
|
|
|
|
|
2025-02-03 11:51:16 +00:00
|
|
|
#include <QtTest/private/qtesthelpers_p.h>
|
|
|
|
|
2024-04-30 11:05:29 +00:00
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
|
2024-04-22 08:32:53 +00:00
|
|
|
constexpr char g_privateKey[] = R"(-----BEGIN RSA PRIVATE KEY-----
|
2024-04-30 11:05:29 +00:00
|
|
|
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
|
|
|
|
-----END RSA PRIVATE KEY-----)";
|
|
|
|
|
2024-04-22 08:32:53 +00:00
|
|
|
constexpr char g_certificate[] = R"(-----BEGIN CERTIFICATE-----
|
2024-04-30 11:05:29 +00:00
|
|
|
MIIFszCCA5ugAwIBAgIUfpP54qSLfus/pFUIBDizbnrDjE4wDQYJKoZIhvcNAQEL
|
|
|
|
BQAwaDELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZyYW5jZTERMA8GA1UEBwwIR3Jl
|
|
|
|
bm9ibGUxFjAUBgNVBAoMDVF0Q29udHJpYnV0b3IxHTAbBgNVBAMMFHFodHRwc3Nl
|
|
|
|
cnZlcnRlc3QuY29tMCAXDTIyMDIwNzE0MzE0NVoYDzIyNjgwNzA3MTQzMTQ1WjBo
|
|
|
|
MQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRnJhbmNlMREwDwYDVQQHDAhHcmVub2Js
|
|
|
|
ZTEWMBQGA1UECgwNUXRDb250cmlidXRvcjEdMBsGA1UEAwwUcWh0dHBzc2VydmVy
|
|
|
|
dGVzdC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC92u1m1Wq7
|
|
|
|
CIbXb693c6NFWJt0rY5vvcM45aBvmsiBD4HZTNw4VrTGyJHJz9tqb0IQSqCflgdK
|
|
|
|
f3kske83d1uwDy+sWohdOSKwUC4cmqyaxhlsAiIJfy3W2VxSZCwmaNnUgqGTfuWA
|
|
|
|
YwqRzbw6H8IdtOuEllZ1DKgFs1vwp6pIeR49i7T8Ey07+WuRcmNXoFeArwGA6ol8
|
|
|
|
6qsYKwG9b6ypnP0PnVkEFVvdgKsFk/bQ1UDnFlSo4XBLeOf72E7VeUlzCIUDdXNT
|
|
|
|
u5JW3bOPOFfSIfDoY5VuNHghK2of/LShr1H2ZBrSGbNDstxX02KCFfmQzw+yBP2L
|
|
|
|
peG/aeayOk+t4tiBeheqkNGsvQ9HQJK2za5xF1+EyFjxWI2hqtnXZnYJNo3GIc8b
|
|
|
|
M1g0k9pAbjSjcsRtDcY/uKBnyCj999JX6RklLmO4AcKxS81QDiC8V9VcvHOgbqp7
|
|
|
|
AtdIJeioPIn1PuDgvzVbX2GwHFWnyAOpUCIGemD4ZMtoLr1Y6b04xcRE5M6WKfuD
|
|
|
|
GVDSlk77b8ze4UWj+FeUgf8xvk08qWk0GqAumojC0Yy6C3sPH0mgzBa8QGXMyB7J
|
|
|
|
xiY1fBIex1DmTEVLm5i5kSuLrZeuT9ZaV6/88MJF1xJSGuNVI2i0VC94YpLALnPY
|
|
|
|
U4AhmKgDSY/GwGLFJpBjx+GJoLnJ1TDgXQIDAQABo1MwUTAdBgNVHQ4EFgQUK7Un
|
|
|
|
0JA3DBUVhclrm6pIZsO60U4wHwYDVR0jBBgwFoAUK7Un0JA3DBUVhclrm6pIZsO6
|
|
|
|
0U4wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAuvSFAgfgurDT
|
|
|
|
/dbXuJ0O+FuGg4NOTNRil5ji3DnMzReIHpSiKiXu76PHHRFnlosvfAFOHlpYntun
|
|
|
|
LhbUAxim/iIgWZR33uzvqXMXBORZ0zffjy2SjGCW8ZJYyTmg9c0tc0jEjv7owtlU
|
|
|
|
m6tUXMOs9U0CzvEKLt0K0dMALaLkXtscuzEWA4PHVvnvTu0Wyjj/8n+DgYzY09kC
|
|
|
|
YF0lJfcG6bddDgspmYyFpULeGGP7+qwgGh4cVBtY5I4Htr3p7hDo6UGDF6AsMQZF
|
|
|
|
1CAEgBVRbJgI2GTnptpm9k3EFKwQ81z5O+NnP3ZsuuZ3CEVaPHyQf/POLAIhmZLt
|
|
|
|
0vS9qoRiS4uMUJDXz2kJFBOFHki073eMvHiKtlpYOlJXMQ4MkHCydjeeuhHcgUCq
|
|
|
|
ZDWuQMmq/8tMwf4YtvxYtXzAMVW9dM8BgWu2G8/JwPMGUGhLfKkHmc8dlQzGDe/W
|
|
|
|
K/uVHlJZNF4Y0eXVlq9DUhpvKOjGc8A208wQlsTUgPxljgJ2+4F3D+t0luc3h65m
|
|
|
|
25iw8eRGuYDoCQLG7u7MI0g8A0H+0h9Xrt8PQql86vmQhmTUhKfedVGOo2t2Bcfn
|
|
|
|
ignL7f4e1m2jh0oWTLhuP1hnVFN4KAKpVIJXhbEkH59cLCN6ARXiEHCM9rmK5Rgk
|
|
|
|
NQZlAZc2w1Ha9lqisaWWpt42QVhQM64=
|
|
|
|
-----END CERTIFICATE-----)";
|
|
|
|
|
|
|
|
#endif // QT_CONFIG(ssl)
|
2018-03-08 17:45:53 +00:00
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2024-04-30 11:05:29 +00:00
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
typedef std::unique_ptr<QSslSocket> QSslSocketPtr;
|
|
|
|
#endif
|
|
|
|
|
2022-06-21 20:37:39 +00:00
|
|
|
using namespace Qt::StringLiterals;
|
2024-04-22 08:32:53 +00:00
|
|
|
using namespace std::chrono_literals;
|
2022-06-21 20:37:39 +00:00
|
|
|
|
2018-03-08 17:45:53 +00:00
|
|
|
class tst_QAbstractHttpServer : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
private slots:
|
2024-04-30 11:05:29 +00:00
|
|
|
void initTestCase();
|
2018-03-08 17:45:53 +00:00
|
|
|
void request_data();
|
|
|
|
void request();
|
|
|
|
void checkListenWarns();
|
|
|
|
void websocket();
|
2024-04-22 08:32:53 +00:00
|
|
|
void verifyWebSocketUpgrades_data();
|
|
|
|
void verifyWebSocketUpgrades();
|
2024-08-29 14:52:47 +00:00
|
|
|
void verifyWebSocketUpgradesGoesOutOfScope();
|
2018-10-20 06:35:55 +00:00
|
|
|
void servers();
|
2020-02-12 08:06:32 +00:00
|
|
|
void qtbug82053();
|
2024-04-30 11:05:29 +00:00
|
|
|
void http2handshake();
|
2024-05-28 14:03:35 +00:00
|
|
|
void http2request();
|
2024-06-12 09:56:04 +00:00
|
|
|
void socketDisconnected();
|
2024-04-30 11:05:29 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
QSslSocketPtr createNewConnection(const QTcpServer *server);
|
|
|
|
bool hasServerAlpn = false;
|
|
|
|
#endif // QT_CONFIG(ssl)
|
2018-03-08 17:45:53 +00:00
|
|
|
};
|
|
|
|
|
2024-04-30 11:05:29 +00:00
|
|
|
void tst_QAbstractHttpServer::initTestCase()
|
|
|
|
{
|
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
hasServerAlpn = QSslSocket::supportedFeatures().contains(QSsl::SupportedFeature::ServerSideAlpn);
|
|
|
|
#endif // QT_CONFIG(ssl)
|
|
|
|
}
|
|
|
|
|
2018-03-08 17:45:53 +00:00
|
|
|
void tst_QAbstractHttpServer::request_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("host");
|
|
|
|
QTest::addColumn<QString>("path");
|
|
|
|
QTest::addColumn<QString>("query");
|
|
|
|
|
|
|
|
QTest::addRow("127.0.0.1") << "127.0.0.1" << "/" << QString();
|
|
|
|
QTest::addRow("0.0.0.0") << "0.0.0.0" << "/" << QString();
|
|
|
|
QTest::addRow("localhost") << "localhost" << "/" << QString();
|
|
|
|
QTest::addRow("localhost with query") << "localhost" << "/" << QString("key=value");
|
2018-07-18 10:47:52 +00:00
|
|
|
QTest::addRow("0.0.0.0 path with spaces") << "0.0.0.0" << "/test test" << QString();
|
|
|
|
QTest::addRow("0.0.0.0 path with spec spaces") << "0.0.0.0" << "/test%20test" << QString();
|
2019-01-30 15:21:43 +00:00
|
|
|
QTest::addRow("127.0.0.1 path with spaces") << "127.0.0.1" << "/test test" << QString();
|
|
|
|
QTest::addRow("127.0.0.1 path with spec spaces") << "127.0.0.1" << "/test%20test" << QString();
|
2018-03-08 17:45:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QAbstractHttpServer::request()
|
|
|
|
{
|
|
|
|
QFETCH(QString, host);
|
|
|
|
QFETCH(QString, path);
|
|
|
|
QFETCH(QString, query);
|
|
|
|
|
2019-01-30 15:21:43 +00:00
|
|
|
#if defined(Q_OS_WIN)
|
2022-06-21 20:37:39 +00:00
|
|
|
if (host == "0.0.0.0"_L1)
|
2019-01-30 15:21:43 +00:00
|
|
|
QSKIP("Windows has problems with 0.0.0.0");
|
|
|
|
#endif
|
|
|
|
|
2018-03-08 17:45:53 +00:00
|
|
|
struct HttpServer : QAbstractHttpServer
|
|
|
|
{
|
|
|
|
QUrl url;
|
|
|
|
QByteArray body;
|
2018-12-20 13:59:07 +00:00
|
|
|
QHttpServerRequest::Method method = QHttpServerRequest::Method::Unknown;
|
2018-03-08 17:45:53 +00:00
|
|
|
quint8 padding[4];
|
|
|
|
|
2022-08-18 13:57:33 +00:00
|
|
|
bool handleRequest(const QHttpServerRequest &request, QHttpServerResponder &responder) override
|
2018-03-08 17:45:53 +00:00
|
|
|
{
|
|
|
|
method = request.method();
|
|
|
|
url = request.url();
|
|
|
|
body = request.body();
|
2022-08-18 13:57:33 +00:00
|
|
|
auto _responder = std::move(responder);
|
2018-03-08 17:45:53 +00:00
|
|
|
return true;
|
|
|
|
}
|
2022-08-19 14:28:46 +00:00
|
|
|
|
2024-08-21 18:22:31 +00:00
|
|
|
void missingHandler(const QHttpServerRequest &, QHttpServerResponder &) override
|
2022-08-18 13:57:33 +00:00
|
|
|
{
|
2022-08-19 14:28:46 +00:00
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
2018-03-08 17:45:53 +00:00
|
|
|
} server;
|
2024-10-09 10:48:05 +00:00
|
|
|
QTcpServer tcpServer;
|
|
|
|
QVERIFY(tcpServer.listen());
|
|
|
|
server.bind(&tcpServer);
|
2018-03-08 17:45:53 +00:00
|
|
|
QNetworkAccessManager networkAccessManager;
|
|
|
|
QUrl url(QStringLiteral("http://%1:%2%3")
|
|
|
|
.arg(host)
|
2024-10-09 10:48:05 +00:00
|
|
|
.arg(tcpServer.serverPort())
|
2018-03-08 17:45:53 +00:00
|
|
|
.arg(path));
|
|
|
|
if (!query.isEmpty())
|
|
|
|
url.setQuery(query);
|
|
|
|
const QNetworkRequest request(url);
|
2018-07-19 20:44:59 +00:00
|
|
|
networkAccessManager.get(request);
|
2018-03-08 17:45:53 +00:00
|
|
|
QTRY_COMPARE(server.method, QHttpServerRequest::Method::Get);
|
|
|
|
QCOMPARE(server.url, url);
|
|
|
|
QCOMPARE(server.body, QByteArray());
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QAbstractHttpServer::checkListenWarns()
|
|
|
|
{
|
|
|
|
struct HttpServer : QAbstractHttpServer
|
|
|
|
{
|
2022-08-18 13:57:33 +00:00
|
|
|
bool handleRequest(const QHttpServerRequest &, QHttpServerResponder &responder) override
|
|
|
|
{
|
|
|
|
auto _responder = std::move(responder);
|
|
|
|
return true;
|
|
|
|
}
|
2022-08-19 14:28:46 +00:00
|
|
|
|
2024-08-21 18:22:31 +00:00
|
|
|
void missingHandler(const QHttpServerRequest &, QHttpServerResponder &) override
|
2022-08-18 13:57:33 +00:00
|
|
|
{
|
2022-08-19 14:28:46 +00:00
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
2018-03-08 17:45:53 +00:00
|
|
|
} server;
|
2024-10-09 10:48:05 +00:00
|
|
|
QTcpServer tcpServer;
|
2019-02-11 13:32:29 +00:00
|
|
|
QTest::ignoreMessage(QtWarningMsg,
|
|
|
|
QRegularExpression(QStringLiteral("The TCP server .* is not listening.")));
|
2024-10-09 10:48:05 +00:00
|
|
|
server.bind(&tcpServer);
|
2018-03-08 17:45:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QAbstractHttpServer::websocket()
|
|
|
|
{
|
|
|
|
#if !defined(QT_WEBSOCKETS_LIB)
|
|
|
|
QSKIP("This test requires WebSocket support");
|
2018-12-20 13:55:41 +00:00
|
|
|
#else
|
2018-03-08 17:45:53 +00:00
|
|
|
struct HttpServer : QAbstractHttpServer
|
|
|
|
{
|
2022-08-18 13:57:33 +00:00
|
|
|
bool handleRequest(const QHttpServerRequest &, QHttpServerResponder &responder) override
|
|
|
|
{
|
|
|
|
auto _responder = std::move(responder);
|
|
|
|
return true;
|
|
|
|
}
|
2022-08-19 14:28:46 +00:00
|
|
|
|
2024-08-21 18:22:31 +00:00
|
|
|
void missingHandler(const QHttpServerRequest &, QHttpServerResponder &) override
|
2022-08-18 13:57:33 +00:00
|
|
|
{
|
2022-08-19 14:28:46 +00:00
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
2018-03-08 17:45:53 +00:00
|
|
|
} server;
|
2024-08-29 14:52:47 +00:00
|
|
|
server.addWebSocketUpgradeVerifier( // Accept all websocket connections
|
|
|
|
&server, [](const QHttpServerRequest &request) {
|
2024-04-22 08:32:53 +00:00
|
|
|
Q_UNUSED(request);
|
|
|
|
return QHttpServerWebSocketUpgradeResponse::accept();
|
|
|
|
});
|
2024-10-09 10:48:05 +00:00
|
|
|
QTcpServer tcpServer;
|
|
|
|
tcpServer.listen();
|
|
|
|
server.bind(&tcpServer);
|
|
|
|
auto makeWebSocket = [this, &tcpServer]() mutable {
|
2024-05-28 12:00:09 +00:00
|
|
|
auto s = std::make_unique<QWebSocket>(QString::fromUtf8(""),
|
|
|
|
QWebSocketProtocol::VersionLatest, this);
|
2024-10-09 10:48:05 +00:00
|
|
|
const QUrl url(QString::fromLatin1("ws://localhost:%1").arg(tcpServer.serverPort()));
|
2019-07-06 17:40:42 +00:00
|
|
|
s->open(url);
|
|
|
|
return s;
|
|
|
|
};
|
|
|
|
|
|
|
|
// We have to send two requests to make sure that swapping between
|
|
|
|
// QTcpSocket and QWebSockets works correctly
|
2024-10-11 13:57:43 +00:00
|
|
|
auto clientSocket1 = makeWebSocket();
|
|
|
|
auto clientSocket2 = makeWebSocket();
|
|
|
|
QVERIFY(clientSocket1);
|
|
|
|
QVERIFY(clientSocket2);
|
2019-07-06 17:40:42 +00:00
|
|
|
|
2018-03-08 17:45:53 +00:00
|
|
|
QSignalSpy newConnectionSpy(&server, &HttpServer::newWebSocketConnection);
|
2022-10-05 05:47:02 +00:00
|
|
|
QTRY_COMPARE(newConnectionSpy.size(), 2);
|
2024-10-11 13:57:43 +00:00
|
|
|
|
|
|
|
std::unique_ptr<QWebSocket> serverSocket1(server.nextPendingWebSocketConnection());
|
|
|
|
QVERIFY(serverSocket1);
|
|
|
|
std::unique_ptr<QWebSocket> serverSocket2(server.nextPendingWebSocketConnection());
|
|
|
|
QVERIFY(serverSocket2);
|
|
|
|
|
|
|
|
QSignalSpy serverSocket1Spy(serverSocket1.get(), &QWebSocket::textMessageReceived);
|
|
|
|
QSignalSpy serverSocket2Spy(serverSocket2.get(), &QWebSocket::textMessageReceived);
|
|
|
|
QSignalSpy clientSocket1Spy(clientSocket1.get(), &QWebSocket::textMessageReceived);
|
|
|
|
QSignalSpy clientSocket2Spy(clientSocket2.get(), &QWebSocket::textMessageReceived);
|
|
|
|
|
|
|
|
// Check that all WebSockets are ready for read/write
|
|
|
|
QTRY_VERIFY(serverSocket1->isValid());
|
|
|
|
QTRY_VERIFY(serverSocket2->isValid());
|
|
|
|
QTRY_VERIFY(clientSocket1->isValid());
|
|
|
|
QTRY_VERIFY(clientSocket2->isValid());
|
|
|
|
|
|
|
|
serverSocket1->sendTextMessage("Server to Client");
|
|
|
|
serverSocket2->sendTextMessage("Server to Client");
|
|
|
|
clientSocket1->sendTextMessage("Client to Server");
|
|
|
|
clientSocket2->sendTextMessage("Client to Server");
|
|
|
|
|
|
|
|
QTRY_COMPARE(clientSocket1Spy.size(), 1);
|
|
|
|
QList<QVariant> clientSocket1Incoming = clientSocket1Spy.takeFirst();
|
|
|
|
QCOMPARE(clientSocket1Incoming.at(0).typeId(), QMetaType::QString);
|
|
|
|
QCOMPARE(clientSocket1Incoming.at(0).toString(), "Server to Client");
|
|
|
|
|
|
|
|
QTRY_COMPARE(clientSocket2Spy.size(), 1);
|
|
|
|
QList<QVariant> clientSocket2Incoming = clientSocket2Spy.takeFirst();
|
|
|
|
QCOMPARE(clientSocket2Incoming.at(0).typeId(), QMetaType::QString);
|
|
|
|
QCOMPARE(clientSocket2Incoming.at(0).toString(), "Server to Client");
|
|
|
|
|
|
|
|
QTRY_COMPARE(serverSocket1Spy.size(), 1);
|
|
|
|
QList<QVariant> serverSocket1Incoming = serverSocket1Spy.takeFirst();
|
|
|
|
QCOMPARE(serverSocket1Incoming.at(0).typeId(), QMetaType::QString);
|
|
|
|
QCOMPARE(serverSocket1Incoming.at(0).toString(), "Client to Server");
|
|
|
|
|
|
|
|
QTRY_COMPARE(serverSocket2Spy.size(), 1);
|
|
|
|
QList<QVariant> serverSocket2Incoming = serverSocket2Spy.takeFirst();
|
|
|
|
QCOMPARE(serverSocket2Incoming.at(0).typeId(), QMetaType::QString);
|
|
|
|
QCOMPARE(serverSocket2Incoming.at(0).toString(), "Client to Server");
|
2018-12-20 13:55:41 +00:00
|
|
|
#endif // defined(QT_WEBSOCKETS_LIB)
|
2018-03-08 17:45:53 +00:00
|
|
|
}
|
|
|
|
|
2024-04-22 08:32:53 +00:00
|
|
|
void tst_QAbstractHttpServer::verifyWebSocketUpgrades_data()
|
|
|
|
{
|
|
|
|
#if !defined(QT_WEBSOCKETS_LIB)
|
|
|
|
QSKIP("This test requires WebSocket support");
|
|
|
|
#endif
|
|
|
|
QTest::addColumn<QString>("url");
|
|
|
|
QTest::addColumn<bool>("useSslPort");
|
|
|
|
QTest::addColumn<bool>("shouldSucceed");
|
|
|
|
QTest::addColumn<bool>("missingHandlerExpected");
|
|
|
|
|
|
|
|
QTest::addRow("allowed") << "ws://localhost:%1/allowed" << false << true << false;
|
|
|
|
QTest::addRow("denied") << "ws://localhost:%1/denied" << false << false << false;
|
|
|
|
QTest::addRow("no match") << "ws://localhost:%1/nomatch" << false << false << true;
|
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
if (QSslSocket::supportsSsl()) {
|
|
|
|
QTest::addRow("sslonly-without") << "ws://localhost:%1/ssl" << false << false << false;
|
|
|
|
QTest::addRow("sslonly-with") << "wss://localhost:%1/ssl" << true << true << false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QAbstractHttpServer::verifyWebSocketUpgrades()
|
|
|
|
{
|
|
|
|
#if defined(QT_WEBSOCKETS_LIB)
|
|
|
|
QFETCH(const QString, url);
|
|
|
|
QFETCH(const bool, useSslPort);
|
|
|
|
QFETCH(const bool, shouldSucceed);
|
|
|
|
QFETCH(const bool, missingHandlerExpected);
|
|
|
|
int port = 0;
|
|
|
|
|
|
|
|
struct HttpServer : QAbstractHttpServer
|
|
|
|
{
|
|
|
|
HttpServer(bool missingExpected) : missingHandlerExpected(missingExpected) { }
|
|
|
|
|
|
|
|
bool handleRequest(const QHttpServerRequest &, QHttpServerResponder &responder) override
|
|
|
|
{
|
|
|
|
auto _responder = std::move(responder);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool missingHandlerExpected = false;
|
2024-08-21 18:22:31 +00:00
|
|
|
void missingHandler(const QHttpServerRequest &, QHttpServerResponder &) override
|
2024-04-22 08:32:53 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(missingHandlerExpected);
|
|
|
|
}
|
|
|
|
} server(missingHandlerExpected);
|
|
|
|
|
2024-08-29 14:52:47 +00:00
|
|
|
server.addWebSocketUpgradeVerifier(&server, [](const QHttpServerRequest &request) {
|
2024-04-22 08:32:53 +00:00
|
|
|
if (request.url().path() == "/allowed"_L1)
|
|
|
|
return QHttpServerWebSocketUpgradeResponse::accept();
|
|
|
|
else
|
|
|
|
return QHttpServerWebSocketUpgradeResponse::passToNext();
|
|
|
|
});
|
2024-08-29 14:52:47 +00:00
|
|
|
server.addWebSocketUpgradeVerifier(&server, [](const QHttpServerRequest &request) {
|
2024-04-22 08:32:53 +00:00
|
|
|
// Explicitly deny
|
|
|
|
if (request.url().path() == "/denied"_L1)
|
|
|
|
return QHttpServerWebSocketUpgradeResponse::deny();
|
|
|
|
else
|
|
|
|
return QHttpServerWebSocketUpgradeResponse::passToNext();
|
|
|
|
});
|
|
|
|
#if QT_CONFIG(ssl)
|
2024-08-29 14:52:47 +00:00
|
|
|
server.addWebSocketUpgradeVerifier(&server, [](const QHttpServerRequest &request) {
|
2024-04-22 08:32:53 +00:00
|
|
|
if (request.url().path() == "/ssl"_L1) {
|
|
|
|
// The QSslConfiguration of a request is null if connection is not using SSL
|
|
|
|
if (request.sslConfiguration().isNull())
|
|
|
|
return QHttpServerWebSocketUpgradeResponse::deny();
|
|
|
|
else
|
|
|
|
return QHttpServerWebSocketUpgradeResponse::accept();
|
|
|
|
}
|
|
|
|
return QHttpServerWebSocketUpgradeResponse::passToNext();
|
|
|
|
});
|
|
|
|
#endif // QT_CONFIG(ssl)
|
|
|
|
QTcpServer tcpServer;
|
|
|
|
tcpServer.listen();
|
|
|
|
server.bind(&tcpServer);
|
|
|
|
#if QT_CONFIG(ssl)
|
2025-02-03 11:51:16 +00:00
|
|
|
if (QTestPrivate::isSecureTransportBlockingTest()) {
|
2025-01-07 07:58:49 +00:00
|
|
|
// We were built with SDK below 15, but a file-based keychains are not working anymore on macOS 15...
|
|
|
|
QSKIP("This test will block in keychain access");
|
|
|
|
}
|
|
|
|
|
2024-04-22 08:32:53 +00:00
|
|
|
QSslServer sslServer;
|
2024-07-09 11:01:38 +00:00
|
|
|
QSslConfiguration sslConfiguration = QSslConfiguration::defaultConfiguration();
|
2024-04-22 08:32:53 +00:00
|
|
|
sslConfiguration.setLocalCertificate(QSslCertificate(QByteArray(g_certificate)));
|
|
|
|
sslConfiguration.setPrivateKey(QSslKey(g_privateKey, QSsl::Rsa));
|
|
|
|
sslServer.setSslConfiguration(sslConfiguration);
|
|
|
|
sslServer.listen();
|
|
|
|
server.bind(&sslServer);
|
|
|
|
if (useSslPort) {
|
|
|
|
port = sslServer.serverPort();
|
|
|
|
} else {
|
|
|
|
port = tcpServer.serverPort();
|
|
|
|
}
|
|
|
|
#else
|
2024-06-18 20:58:12 +00:00
|
|
|
QVERIFY(!useSslPort);
|
2024-04-22 08:32:53 +00:00
|
|
|
port = tcpServer.serverPort();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
auto makeWebSocket = [&, this]() mutable {
|
|
|
|
auto s = std::make_unique<QWebSocket>(QString::fromUtf8(""),
|
|
|
|
QWebSocketProtocol::VersionLatest, this);
|
|
|
|
const QUrl qurl(url.arg(port));
|
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
if (useSslPort) {
|
|
|
|
const QList<QSslError> expectedSslErrors = {
|
|
|
|
QSslError(QSslError::SelfSignedCertificate, QSslCertificate(g_certificate)),
|
|
|
|
// Non-OpenSSL backends are not able to report a specific error code
|
|
|
|
// for self-signed certificates.
|
|
|
|
QSslError(QSslError::CertificateUntrusted, QSslCertificate(g_certificate)),
|
|
|
|
QSslError(QSslError::HostNameMismatch, QSslCertificate(g_certificate)),
|
|
|
|
};
|
|
|
|
s->ignoreSslErrors(expectedSslErrors);
|
|
|
|
}
|
|
|
|
#endif // QT_CONFIG(ssl)
|
|
|
|
s->open(qurl);
|
|
|
|
return s;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto s1 = makeWebSocket();
|
|
|
|
auto s2 = makeWebSocket();
|
|
|
|
|
|
|
|
QSignalSpy newConnectionSpy(&server, &HttpServer::newWebSocketConnection);
|
|
|
|
if (shouldSucceed) {
|
|
|
|
QTRY_COMPARE(newConnectionSpy.size(), 2);
|
|
|
|
server.nextPendingWebSocketConnection();
|
|
|
|
server.nextPendingWebSocketConnection();
|
|
|
|
} else {
|
|
|
|
QTest::qWait(useSslPort ? 2s : 1s);
|
|
|
|
QCOMPARE(newConnectionSpy.size(), 0);
|
|
|
|
}
|
|
|
|
#endif // defined(QT_WEBSOCKETS_LIB)
|
|
|
|
}
|
|
|
|
|
2024-08-29 14:52:47 +00:00
|
|
|
void tst_QAbstractHttpServer::verifyWebSocketUpgradesGoesOutOfScope()
|
|
|
|
{
|
|
|
|
#if defined(QT_WEBSOCKETS_LIB)
|
|
|
|
const QString url("ws://localhost:%1/allowed");
|
|
|
|
int port = 0;
|
|
|
|
|
|
|
|
struct HttpServer : QAbstractHttpServer
|
|
|
|
{
|
|
|
|
bool handleRequest(const QHttpServerRequest &, QHttpServerResponder &responder) override
|
|
|
|
{
|
|
|
|
auto _responder = std::move(responder);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool missingHandlerExpected = false;
|
|
|
|
void missingHandler(const QHttpServerRequest &, QHttpServerResponder &) override
|
|
|
|
{
|
|
|
|
Q_ASSERT(missingHandlerExpected);
|
|
|
|
}
|
|
|
|
|
|
|
|
} server;
|
|
|
|
|
|
|
|
auto makeWebSocket = [&, this]() mutable {
|
|
|
|
auto s = std::make_unique<QWebSocket>(QString::fromUtf8(""),
|
|
|
|
QWebSocketProtocol::VersionLatest, this);
|
|
|
|
const QUrl qurl(url.arg(port));
|
|
|
|
|
|
|
|
s->open(qurl);
|
|
|
|
return s;
|
|
|
|
};
|
|
|
|
|
|
|
|
QTcpServer tcpServer;
|
|
|
|
tcpServer.listen();
|
|
|
|
server.bind(&tcpServer);
|
|
|
|
port = tcpServer.serverPort();
|
|
|
|
|
|
|
|
{ // Verifier is in scope
|
|
|
|
struct WebSocketVerifier : public QObject
|
|
|
|
{
|
|
|
|
QHttpServerWebSocketUpgradeResponse verify(const QHttpServerRequest &request)
|
|
|
|
{
|
|
|
|
if (request.url().path() == "/allowed"_L1)
|
|
|
|
return QHttpServerWebSocketUpgradeResponse::accept();
|
|
|
|
else
|
|
|
|
return QHttpServerWebSocketUpgradeResponse::passToNext();
|
|
|
|
}
|
|
|
|
} verifier;
|
|
|
|
|
|
|
|
server.addWebSocketUpgradeVerifier(&verifier, &WebSocketVerifier::verify);
|
|
|
|
|
|
|
|
auto s1 = makeWebSocket();
|
|
|
|
auto s2 = makeWebSocket();
|
|
|
|
|
|
|
|
QSignalSpy newConnectionSpy(&server, &HttpServer::newWebSocketConnection);
|
|
|
|
QTRY_COMPARE(newConnectionSpy.size(), 2); // Success
|
|
|
|
server.nextPendingWebSocketConnection();
|
|
|
|
server.nextPendingWebSocketConnection();
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // Verifier is out of scope
|
|
|
|
server.missingHandlerExpected = true;
|
|
|
|
|
|
|
|
auto s1 = makeWebSocket();
|
|
|
|
auto s2 = makeWebSocket();
|
|
|
|
|
|
|
|
QSignalSpy newConnectionSpy(&server, &HttpServer::newWebSocketConnection);
|
|
|
|
QTest::qWait(2s);
|
|
|
|
QCOMPARE(newConnectionSpy.size(), 0); // Failure
|
|
|
|
}
|
|
|
|
#endif // defined(QT_WEBSOCKETS_LIB)
|
|
|
|
}
|
|
|
|
|
2018-10-20 06:35:55 +00:00
|
|
|
void tst_QAbstractHttpServer::servers()
|
|
|
|
{
|
|
|
|
struct HttpServer : QAbstractHttpServer
|
|
|
|
{
|
2022-08-18 13:57:33 +00:00
|
|
|
bool handleRequest(const QHttpServerRequest &, QHttpServerResponder &responder) override
|
|
|
|
{
|
|
|
|
auto _responder = std::move(responder);
|
|
|
|
return true;
|
|
|
|
}
|
2022-08-19 14:28:46 +00:00
|
|
|
|
2024-08-21 18:22:31 +00:00
|
|
|
void missingHandler(const QHttpServerRequest &, QHttpServerResponder &) override
|
2022-08-18 13:57:33 +00:00
|
|
|
{
|
2022-08-19 14:28:46 +00:00
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
2018-10-20 06:35:55 +00:00
|
|
|
} server;
|
2024-10-09 10:48:05 +00:00
|
|
|
QTcpServer tcpServer;
|
|
|
|
tcpServer.listen();
|
|
|
|
server.bind(&tcpServer);
|
|
|
|
QTcpServer tcpServer2;
|
|
|
|
tcpServer2.listen();
|
|
|
|
server.bind(&tcpServer2);
|
2022-10-05 05:47:02 +00:00
|
|
|
QTRY_COMPARE(server.servers().size(), 2);
|
|
|
|
QTRY_COMPARE(server.serverPorts().size(), 2);
|
2024-10-09 10:48:05 +00:00
|
|
|
QTRY_COMPARE(server.servers().first(), &tcpServer);
|
|
|
|
QTRY_COMPARE(server.serverPorts().first(), tcpServer.serverPort());
|
|
|
|
QTRY_COMPARE(server.servers().last(), &tcpServer2);
|
|
|
|
QTRY_COMPARE(server.serverPorts().last(), tcpServer2.serverPort());
|
2018-10-20 06:35:55 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 08:06:32 +00:00
|
|
|
void tst_QAbstractHttpServer::qtbug82053()
|
|
|
|
{
|
|
|
|
struct HttpServer : QAbstractHttpServer
|
|
|
|
{
|
2020-02-17 21:16:55 +00:00
|
|
|
bool wasConnectRequest{false};
|
2022-08-18 13:57:33 +00:00
|
|
|
bool handleRequest(const QHttpServerRequest &req, QHttpServerResponder &responder) override
|
2020-02-17 21:16:55 +00:00
|
|
|
{
|
2022-08-18 13:57:33 +00:00
|
|
|
auto _responder = std::move(responder);
|
2020-02-17 21:16:55 +00:00
|
|
|
wasConnectRequest = (req.method() == QHttpServerRequest::Method::Connect);
|
|
|
|
return false;
|
|
|
|
}
|
2022-08-19 14:28:46 +00:00
|
|
|
|
2024-08-21 18:22:31 +00:00
|
|
|
void missingHandler(const QHttpServerRequest &, QHttpServerResponder &) override { }
|
2020-02-12 08:06:32 +00:00
|
|
|
} server;
|
2024-10-09 10:48:05 +00:00
|
|
|
QTcpServer tcpServer;
|
|
|
|
tcpServer.listen();
|
|
|
|
server.bind(&tcpServer);
|
2020-02-12 08:06:32 +00:00
|
|
|
|
2020-02-17 21:23:30 +00:00
|
|
|
QTcpSocket client;
|
2024-10-09 10:48:05 +00:00
|
|
|
client.connectToHost(QHostAddress::LocalHost, tcpServer.serverPort());
|
2020-02-17 21:23:30 +00:00
|
|
|
client.waitForConnected();
|
|
|
|
client.write("CONNECT / HTTP/1.1\n\n");
|
|
|
|
client.waitForBytesWritten();
|
2020-02-12 08:06:32 +00:00
|
|
|
QTest::qWait(0);
|
2020-02-17 21:16:55 +00:00
|
|
|
QCOMPARE(client.state(), QAbstractSocket::ConnectedState);
|
|
|
|
QTRY_VERIFY(server.wasConnectRequest);
|
2020-02-12 08:06:32 +00:00
|
|
|
}
|
|
|
|
|
2024-04-30 11:05:29 +00:00
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
QSslSocketPtr tst_QAbstractHttpServer::createNewConnection(const QTcpServer * server)
|
|
|
|
{
|
|
|
|
QSslSocketPtr socketPtr = std::make_unique<QSslSocket>();
|
|
|
|
|
|
|
|
QSslConfiguration clientConfig = socketPtr->sslConfiguration();
|
2024-07-09 11:01:38 +00:00
|
|
|
clientConfig.setAllowedNextProtocols({ QSslConfiguration::ALPNProtocolHTTP2 });
|
2024-04-30 11:05:29 +00:00
|
|
|
socketPtr->setSslConfiguration(clientConfig);
|
|
|
|
socketPtr->connectToHostEncrypted(server->serverAddress().toString(),
|
2024-05-28 14:03:35 +00:00
|
|
|
server->serverPort());
|
2024-04-30 11:05:29 +00:00
|
|
|
socketPtr->waitForConnected();
|
|
|
|
|
|
|
|
// Expected errors
|
|
|
|
connect(socketPtr.get(), &QSslSocket::sslErrors, this , [socket = socketPtr.get()]() {
|
|
|
|
socket->ignoreSslErrors();
|
|
|
|
});
|
|
|
|
|
|
|
|
QTestEventLoop loop;
|
|
|
|
int waitFor = 2;
|
|
|
|
connect(socketPtr.get(), &QSslSocket::encrypted, &loop, [&loop, &waitFor]() {
|
|
|
|
if (!--waitFor)
|
|
|
|
loop.exitLoop();
|
|
|
|
});
|
|
|
|
connect(server, &QTcpServer::pendingConnectionAvailable, &loop, [&loop, &waitFor]() {
|
|
|
|
if (!--waitFor)
|
|
|
|
loop.exitLoop();
|
|
|
|
});
|
|
|
|
loop.enterLoop(5);
|
|
|
|
|
|
|
|
return socketPtr;
|
|
|
|
}
|
|
|
|
#endif // QT_CONFIG(ssl)
|
|
|
|
|
|
|
|
void tst_QAbstractHttpServer::http2handshake()
|
|
|
|
{
|
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
if (!hasServerAlpn)
|
|
|
|
QSKIP("Server-side ALPN is unsupported, skipping test");
|
|
|
|
|
|
|
|
struct HttpServer : QAbstractHttpServer
|
|
|
|
{
|
|
|
|
bool handleRequest(const QHttpServerRequest &, QHttpServerResponder &) override
|
|
|
|
{ return false; }
|
2024-08-21 18:22:31 +00:00
|
|
|
void missingHandler(const QHttpServerRequest &, QHttpServerResponder &) override { }
|
2024-04-30 11:05:29 +00:00
|
|
|
} server;
|
|
|
|
|
2024-07-09 11:01:38 +00:00
|
|
|
auto sslserver = std::make_unique<QSslServer>();
|
|
|
|
QSslConfiguration serverConfig = QSslConfiguration::defaultConfiguration();
|
2024-04-30 11:05:29 +00:00
|
|
|
serverConfig.setLocalCertificate(QSslCertificate(g_certificate));
|
|
|
|
serverConfig.setPrivateKey(QSslKey(g_privateKey, QSsl::Rsa));
|
2024-07-09 11:01:38 +00:00
|
|
|
serverConfig.setAllowedNextProtocols({ QSslConfiguration::ALPNProtocolHTTP2 });
|
|
|
|
sslserver->setSslConfiguration(serverConfig);
|
|
|
|
QVERIFY2(sslserver->listen(QHostAddress::LocalHost), "HTTPS server listen failed");
|
|
|
|
QVERIFY2(server.bind(sslserver.get()), "HTTPS server bind failed");
|
|
|
|
sslserver.release();
|
2024-04-30 11:05:29 +00:00
|
|
|
|
|
|
|
const auto serverPtr = server.servers().constFirst();
|
|
|
|
QSslSocketPtr client1 = createNewConnection(serverPtr);
|
|
|
|
// Check client socket
|
|
|
|
QVERIFY(client1->isEncrypted());
|
|
|
|
QCOMPARE(client1->state(), QAbstractSocket::ConnectedState);
|
|
|
|
|
|
|
|
// HTTP/2 Client Preface
|
|
|
|
client1->write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
|
|
|
|
client1->waitForBytesWritten();
|
|
|
|
|
|
|
|
QTestEventLoop loop;
|
|
|
|
connect(client1.get(), &QAbstractSocket::readyRead, &loop, &QTestEventLoop::exitLoop);
|
|
|
|
loop.enterLoop(5);
|
|
|
|
|
|
|
|
// HTTP/2 Server Preface
|
|
|
|
QVERIFY(client1->bytesAvailable() != 0);
|
|
|
|
#ifdef QT_BUILD_INTERNAL
|
|
|
|
auto serverPrefaceResult = client1->readAll();
|
|
|
|
auto serverPreface = Http2::configurationToSettingsFrame(QHttp2Configuration()).buffer;
|
|
|
|
auto serverPrefaceExpected = QByteArrayView(reinterpret_cast<const char *>(&serverPreface[0]),
|
|
|
|
serverPreface.size());
|
|
|
|
QCOMPARE(serverPrefaceResult, serverPrefaceExpected);
|
2024-05-15 11:09:09 +00:00
|
|
|
|
|
|
|
// Check settings send in server's preface
|
|
|
|
QHttp2Configuration h2config;
|
|
|
|
constexpr quint32 MaxFrameSize = 16394;
|
|
|
|
constexpr bool ServerPushEnabled = true;
|
|
|
|
constexpr quint32 StreamReceiveWindowSize = 50000;
|
|
|
|
h2config.setMaxFrameSize(MaxFrameSize);
|
|
|
|
h2config.setServerPushEnabled(ServerPushEnabled);
|
|
|
|
h2config.setStreamReceiveWindowSize(StreamReceiveWindowSize);
|
|
|
|
server.setHttp2Configuration(h2config);
|
|
|
|
|
|
|
|
QSslSocketPtr client2 = createNewConnection(serverPtr);
|
|
|
|
QVERIFY(client2->isEncrypted());
|
|
|
|
QCOMPARE(client2->state(), QAbstractSocket::ConnectedState);
|
|
|
|
|
|
|
|
// HTTP/2 Client Preface
|
|
|
|
client2->write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
|
|
|
|
client2->waitForBytesWritten();
|
|
|
|
|
|
|
|
connect(client2.get(), &QAbstractSocket::readyRead, &loop, &QTestEventLoop::exitLoop);
|
|
|
|
loop.enterLoop(5);
|
|
|
|
|
|
|
|
// HTTP/2 Server Preface
|
|
|
|
QVERIFY(client2->bytesAvailable() != 0);
|
|
|
|
serverPrefaceResult = client2->readAll();
|
|
|
|
serverPreface = Http2::configurationToSettingsFrame(h2config).buffer;
|
|
|
|
serverPrefaceExpected = QByteArrayView(reinterpret_cast<const char *>(&serverPreface[0]),
|
|
|
|
serverPreface.size());
|
|
|
|
QCOMPARE(serverPrefaceResult, serverPrefaceExpected);
|
2024-04-30 11:05:29 +00:00
|
|
|
#endif // QT_BUILD_INTERNAL
|
|
|
|
|
|
|
|
#else
|
|
|
|
QSKIP("TLS/SSL is not available, skipping test");
|
|
|
|
#endif // QT_CONFIG(ssl)
|
|
|
|
}
|
|
|
|
|
2024-05-28 14:03:35 +00:00
|
|
|
void tst_QAbstractHttpServer::http2request()
|
|
|
|
{
|
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
if (!hasServerAlpn)
|
|
|
|
QSKIP("Server-side ALPN is unsupported, skipping test");
|
|
|
|
|
|
|
|
struct HttpServer : QAbstractHttpServer
|
|
|
|
{
|
|
|
|
QUrl url;
|
|
|
|
QHttpHeaders headers;
|
|
|
|
QByteArray body = {"hello client"};
|
|
|
|
bool receivedRequest = false;
|
|
|
|
|
|
|
|
bool handleRequest(const QHttpServerRequest &request, QHttpServerResponder &responder) override
|
|
|
|
{
|
|
|
|
receivedRequest = true;
|
|
|
|
url = request.url();
|
|
|
|
|
|
|
|
headers.append("name1", "value1");
|
|
|
|
headers.append("name2", "value2");
|
|
|
|
responder.write(body, headers, QHttpServerResponder::StatusCode::Ok);
|
|
|
|
|
|
|
|
auto _responder = std::move(responder);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-08-21 18:22:31 +00:00
|
|
|
void missingHandler(const QHttpServerRequest &, QHttpServerResponder &) override
|
2024-05-28 14:03:35 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
|
|
|
} server;
|
|
|
|
|
2024-07-09 11:01:38 +00:00
|
|
|
auto sslserver = std::make_unique<QSslServer>();
|
|
|
|
QSslConfiguration serverConfig = QSslConfiguration::defaultConfiguration();
|
2024-05-28 14:03:35 +00:00
|
|
|
serverConfig.setLocalCertificate(QSslCertificate(g_certificate));
|
|
|
|
serverConfig.setPrivateKey(QSslKey(g_privateKey, QSsl::Rsa));
|
2024-07-09 11:01:38 +00:00
|
|
|
serverConfig.setAllowedNextProtocols({ QSslConfiguration::ALPNProtocolHTTP2 });
|
|
|
|
sslserver->setSslConfiguration(serverConfig);
|
|
|
|
QVERIFY2(sslserver->listen(QHostAddress::LocalHost), "HTTPS server listen failed");
|
|
|
|
quint16 port = sslserver->serverPort();
|
|
|
|
QVERIFY2(server.bind(sslserver.get()), "HTTPS server bind failed");
|
|
|
|
sslserver.release();
|
2024-05-28 14:03:35 +00:00
|
|
|
|
|
|
|
const auto serverPtr = server.servers().constFirst();
|
|
|
|
QNetworkAccessManager manager;
|
|
|
|
const QList<QSslError> expectedSslErrors = {
|
|
|
|
QSslError(QSslError::SelfSignedCertificate, QSslCertificate(g_certificate)),
|
|
|
|
QSslError(QSslError::CertificateUntrusted, QSslCertificate(g_certificate)),
|
|
|
|
QSslError(QSslError::HostNameMismatch, QSslCertificate(g_certificate)),
|
|
|
|
};
|
|
|
|
connect(&manager, &QNetworkAccessManager::sslErrors,
|
|
|
|
this, [expectedSslErrors](QNetworkReply *reply, const QList<QSslError> &errors) {
|
|
|
|
for (const auto &error: errors) {
|
|
|
|
if (!expectedSslErrors.contains(error))
|
|
|
|
qCritical() << "Got unexpected ssl error:" << error << error.certificate();
|
|
|
|
}
|
|
|
|
reply->ignoreSslErrors(expectedSslErrors);
|
|
|
|
});
|
|
|
|
|
|
|
|
QUrl url;
|
|
|
|
url.setScheme("https");
|
|
|
|
url.setHost(serverPtr->serverAddress().toString());
|
|
|
|
url.setPort(port);
|
|
|
|
url.setPath("/foo");
|
|
|
|
QNetworkRequest req(url);
|
|
|
|
QNetworkReply *reply = manager.get(req);
|
|
|
|
QTRY_VERIFY(reply->isFinished());
|
|
|
|
|
|
|
|
const QVariant http2Used = reply->attribute(QNetworkRequest::Http2WasUsedAttribute);
|
|
|
|
QVERIFY(http2Used.isValid());
|
|
|
|
QVERIFY(http2Used.toBool());
|
|
|
|
|
|
|
|
QCOMPARE(server.receivedRequest, true);
|
|
|
|
QCOMPARE(server.url, url);
|
|
|
|
|
|
|
|
QCOMPARE(reply->readAll(), server.body);
|
|
|
|
QHttpHeaders expectedHeaders = server.headers;
|
|
|
|
expectedHeaders.append(QHttpHeaders::WellKnownHeader::ContentLength,
|
|
|
|
QByteArray::number(server.body.size()));
|
|
|
|
QCOMPARE(reply->headers().toListOfPairs(), expectedHeaders.toListOfPairs());
|
|
|
|
#else
|
|
|
|
QSKIP("TLS/SSL is not available, skipping test");
|
|
|
|
#endif // QT_CONFIG(ssl)
|
|
|
|
}
|
|
|
|
|
2024-06-12 09:56:04 +00:00
|
|
|
void tst_QAbstractHttpServer::socketDisconnected()
|
|
|
|
{
|
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
if (!hasServerAlpn)
|
|
|
|
QSKIP("Server-side ALPN is unsupported, skipping test");
|
|
|
|
|
|
|
|
struct HttpServer : QAbstractHttpServer
|
|
|
|
{
|
|
|
|
bool handlingStarted = false;
|
|
|
|
bool handlingFinished = false;
|
|
|
|
|
|
|
|
bool handleRequest(const QHttpServerRequest &, QHttpServerResponder &responder) override
|
|
|
|
{
|
|
|
|
handlingStarted = true;
|
|
|
|
|
|
|
|
QTestEventLoop loop;
|
|
|
|
loop.enterLoopMSecs(500);
|
|
|
|
|
|
|
|
responder.write(QHttpServerResponder::StatusCode::Ok);
|
|
|
|
|
|
|
|
handlingFinished = true;
|
|
|
|
auto _responder = std::move(responder);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-08-21 18:22:31 +00:00
|
|
|
void missingHandler(const QHttpServerRequest &, QHttpServerResponder &) override
|
2024-06-12 09:56:04 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
|
|
|
} server;
|
|
|
|
|
2024-07-09 11:01:38 +00:00
|
|
|
auto sslserver = std::make_unique<QSslServer>();
|
|
|
|
QSslConfiguration serverConfig = QSslConfiguration::defaultConfiguration();
|
2024-06-12 09:56:04 +00:00
|
|
|
serverConfig.setLocalCertificate(QSslCertificate(g_certificate));
|
|
|
|
serverConfig.setPrivateKey(QSslKey(g_privateKey, QSsl::Rsa));
|
2024-07-09 11:01:38 +00:00
|
|
|
serverConfig.setAllowedNextProtocols({ QSslConfiguration::ALPNProtocolHTTP2 });
|
|
|
|
sslserver->setSslConfiguration(serverConfig);
|
|
|
|
QVERIFY2(sslserver->listen(QHostAddress::LocalHost), "HTTPS server listen failed");
|
|
|
|
QVERIFY2(server.bind(sslserver.get()), "HTTPS server bind failed");
|
|
|
|
sslserver.release();
|
2024-06-12 09:56:04 +00:00
|
|
|
|
|
|
|
const auto serverPtr = server.servers().constFirst();
|
|
|
|
QSslSocketPtr socket = createNewConnection(serverPtr);
|
|
|
|
QVERIFY(socket->isEncrypted());
|
|
|
|
QCOMPARE(socket->state(), QAbstractSocket::ConnectedState);
|
|
|
|
|
|
|
|
QHttp2Connection *connection = QHttp2Connection::createDirectConnection(socket.get(), {});
|
|
|
|
|
|
|
|
QSignalSpy settingsFrameReceivedSpy{ connection, &QHttp2Connection::settingsFrameReceived };
|
|
|
|
connect(socket.get(), &QIODevice::readyRead, connection, &QHttp2Connection::handleReadyRead);
|
|
|
|
connection->handleReadyRead();
|
|
|
|
|
|
|
|
auto stream = connection->createStream().unwrap();
|
|
|
|
QVERIFY(stream);
|
|
|
|
|
2024-10-15 14:26:07 +00:00
|
|
|
QVERIFY(settingsFrameReceivedSpy.wait());
|
|
|
|
|
2024-06-12 09:56:04 +00:00
|
|
|
// disconnect while request is being processed on server
|
|
|
|
QTimer timer;
|
|
|
|
connect(&timer, &QTimer::timeout, this, [&server, &socket, &timer] {
|
|
|
|
if (server.handlingStarted) {
|
|
|
|
socket->disconnectFromHost();
|
|
|
|
timer.stop();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
timer.start(100);
|
|
|
|
|
|
|
|
HPack::HttpHeader headers = HPack::HttpHeader{
|
|
|
|
{ ":authority", "example.com" },
|
|
|
|
{ ":method", "GET" },
|
|
|
|
{ ":path", "/" },
|
|
|
|
{ ":scheme", "https" },
|
|
|
|
};
|
|
|
|
stream->sendHEADERS(headers, true);
|
|
|
|
|
|
|
|
QTRY_VERIFY(server.handlingFinished);
|
|
|
|
|
|
|
|
#else
|
|
|
|
QSKIP("TLS/SSL is not available, skipping test");
|
|
|
|
#endif // QT_CONFIG(ssl)
|
|
|
|
}
|
|
|
|
|
2018-03-08 17:45:53 +00:00
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
|
|
QTEST_MAIN(tst_QAbstractHttpServer)
|
|
|
|
|
|
|
|
#include "tst_qabstracthttpserver.moc"
|