diff options
Diffstat (limited to 'tests/auto/network')
16 files changed, 917 insertions, 177 deletions
diff --git a/tests/auto/network/access/access.pro b/tests/auto/network/access/access.pro index 1d78cf253b..b140b5e9f2 100644 --- a/tests/auto/network/access/access.pro +++ b/tests/auto/network/access/access.pro @@ -13,11 +13,13 @@ SUBDIRS=\ qhttpnetworkreply \ qabstractnetworkcache \ hpack \ - http2 + http2 \ + hsts !qtConfig(private_tests): SUBDIRS -= \ qhttpnetworkconnection \ qhttpnetworkreply \ qftp \ hpack \ - http2 + http2 \ + hsts diff --git a/tests/auto/network/access/hsts/hsts.pro b/tests/auto/network/access/hsts/hsts.pro new file mode 100644 index 0000000000..07bdea5f62 --- /dev/null +++ b/tests/auto/network/access/hsts/hsts.pro @@ -0,0 +1,6 @@ +QT += core core-private network network-private testlib +CONFIG += testcase parallel_test c++11 +TEMPLATE = app +TARGET = tst_qhsts + +SOURCES += tst_qhsts.cpp diff --git a/tests/auto/network/access/hsts/tst_qhsts.cpp b/tests/auto/network/access/hsts/tst_qhsts.cpp new file mode 100644 index 0000000000..656516f46b --- /dev/null +++ b/tests/auto/network/access/hsts/tst_qhsts.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QtCore/qdatetime.h> +#include <QtCore/qvector.h> +#include <QtCore/qpair.h> +#include <QtCore/qurl.h> + +#include <QtNetwork/private/qhsts_p.h> + +QT_USE_NAMESPACE + +class tst_QHsts : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testSingleKnownHost_data(); + void testSingleKnownHost(); + void testMultilpeKnownHosts(); + void testPolicyExpiration(); + void testSTSHeaderParser(); +}; + +void tst_QHsts::testSingleKnownHost_data() +{ + QTest::addColumn<QUrl>("knownHost"); + QTest::addColumn<QDateTime>("policyExpires"); + QTest::addColumn<bool>("includeSubDomains"); + QTest::addColumn<QUrl>("hostToTest"); + QTest::addColumn<bool>("isKnown"); + + const QDateTime currentUTC = QDateTime::currentDateTimeUtc(); + const QUrl knownHost(QLatin1String("http://example.com")); + const QUrl validSubdomain(QLatin1String("https://sub.example.com/ohoho")); + const QUrl unknownDomain(QLatin1String("http://example.org")); + const QUrl subSubdomain(QLatin1String("https://level3.level2.example.com")); + + const QDateTime validDate(currentUTC.addSecs(1000)); + QTest::newRow("same-known") << knownHost << validDate << false << knownHost << true; + QTest::newRow("subexcluded") << knownHost << validDate << false << validSubdomain << false; + QTest::newRow("subincluded") << knownHost << validDate << true << validSubdomain << true; + QTest::newRow("unknown-subexcluded") << knownHost << validDate << false << unknownDomain << false; + QTest::newRow("unknown-subincluded") << knownHost << validDate << true << unknownDomain << false; + QTest::newRow("sub-subdomain-subincluded") << knownHost << validDate << true << subSubdomain << true; + QTest::newRow("sub-subdomain-subexcluded") << knownHost << validDate << false << subSubdomain << false; + + const QDateTime invalidDate; + QTest::newRow("invalid-time") << knownHost << invalidDate << false << knownHost << false; + QTest::newRow("invalid-time-subexcluded") << knownHost << invalidDate << false + << validSubdomain << false; + QTest::newRow("invalid-time-subincluded") << knownHost << invalidDate << true + << validSubdomain << false; + + const QDateTime expiredDate(currentUTC.addSecs(-1000)); + QTest::newRow("expired-time") << knownHost << expiredDate << false << knownHost << false; + QTest::newRow("expired-time-subexcluded") << knownHost << expiredDate << false + << validSubdomain << false; + QTest::newRow("expired-time-subincluded") << knownHost << expiredDate << true + << validSubdomain << false; + const QUrl ipAsHost(QLatin1String("http://127.0.0.1")); + QTest::newRow("ip-address-in-hostname") << ipAsHost << validDate << false + << ipAsHost << false; + + const QUrl anyIPv4AsHost(QLatin1String("http://0.0.0.0")); + QTest::newRow("anyip4-address-in-hostname") << anyIPv4AsHost << validDate + << false << anyIPv4AsHost << false; + const QUrl anyIPv6AsHost(QLatin1String("http://[::]")); + QTest::newRow("anyip6-address-in-hostname") << anyIPv6AsHost << validDate + << false << anyIPv6AsHost << false; + +} + +void tst_QHsts::testSingleKnownHost() +{ + QFETCH(const QUrl, knownHost); + QFETCH(const QDateTime, policyExpires); + QFETCH(const bool, includeSubDomains); + QFETCH(const QUrl, hostToTest); + QFETCH(const bool, isKnown); + + QHstsCache cache; + cache.updateKnownHost(knownHost, policyExpires, includeSubDomains); + QCOMPARE(cache.isKnownHost(hostToTest), isKnown); +} + +void tst_QHsts::testMultilpeKnownHosts() +{ + const QDateTime currentUTC = QDateTime::currentDateTimeUtc(); + const QDateTime validDate(currentUTC.addSecs(10000)); + const QDateTime expiredDate(currentUTC.addSecs(-10000)); + const QUrl exampleCom(QLatin1String("https://example.com")); + const QUrl subExampleCom(QLatin1String("https://sub.example.com")); + + QHstsCache cache; + // example.com is HSTS and includes subdomains: + cache.updateKnownHost(exampleCom, validDate, true); + QVERIFY(cache.isKnownHost(exampleCom)); + QVERIFY(cache.isKnownHost(subExampleCom)); + // example.com can set its policy not to include subdomains: + cache.updateKnownHost(exampleCom, validDate, false); + QVERIFY(!cache.isKnownHost(subExampleCom)); + // but sub.example.com can set its own policy: + cache.updateKnownHost(subExampleCom, validDate, false); + QVERIFY(cache.isKnownHost(subExampleCom)); + // let's say example.com's policy has expired: + cache.updateKnownHost(exampleCom, expiredDate, false); + QVERIFY(!cache.isKnownHost(exampleCom)); + // it should not affect sub.example.com's policy: + QVERIFY(cache.isKnownHost(subExampleCom)); + + // clear cache and invalidate all policies: + cache.clear(); + QVERIFY(!cache.isKnownHost(exampleCom)); + QVERIFY(!cache.isKnownHost(subExampleCom)); + + // siblings: + const QUrl anotherSub(QLatin1String("https://sub2.example.com")); + cache.updateKnownHost(subExampleCom, validDate, true); + cache.updateKnownHost(anotherSub, validDate, true); + QVERIFY(cache.isKnownHost(subExampleCom)); + QVERIFY(cache.isKnownHost(anotherSub)); + // they cannot set superdomain's policy: + QVERIFY(!cache.isKnownHost(exampleCom)); + // a sibling cannot set another sibling's policy: + cache.updateKnownHost(anotherSub, expiredDate, false); + QVERIFY(cache.isKnownHost(subExampleCom)); + QVERIFY(!cache.isKnownHost(anotherSub)); + QVERIFY(!cache.isKnownHost(exampleCom)); + // let's make example.com known again: + cache.updateKnownHost(exampleCom, validDate, true); + // a subdomain cannot affect its superdomain's policy: + cache.updateKnownHost(subExampleCom, expiredDate, true); + QVERIFY(cache.isKnownHost(exampleCom)); + // and this superdomain includes subdomains in its HSTS policy: + QVERIFY(cache.isKnownHost(subExampleCom)); + QVERIFY(cache.isKnownHost(anotherSub)); + + // a subdomain (with its subdomains) cannot affect its superdomain's policy: + cache.updateKnownHost(exampleCom, expiredDate, true); + cache.updateKnownHost(subExampleCom, validDate, true); + QVERIFY(cache.isKnownHost(subExampleCom)); + QVERIFY(!cache.isKnownHost(exampleCom)); +} + +void tst_QHsts::testPolicyExpiration() +{ + QDateTime currentUTC = QDateTime::currentDateTimeUtc(); + const QUrl exampleCom(QLatin1String("http://example.com")); + const QUrl subdomain(QLatin1String("http://subdomain.example.com")); + const qint64 lifeTimeMS = 50; + + QHstsCache cache; + // start with 'includeSubDomains' and 5 s. lifetime: + cache.updateKnownHost(exampleCom, currentUTC.addMSecs(lifeTimeMS), true); + QVERIFY(cache.isKnownHost(exampleCom)); + QVERIFY(cache.isKnownHost(subdomain)); + // wait for approx. a half of lifetime: + QTest::qWait(lifeTimeMS / 2); + + if (QDateTime::currentDateTimeUtc() < currentUTC.addMSecs(lifeTimeMS)) { + // Should still be valid: + QVERIFY(cache.isKnownHost(exampleCom)); + QVERIFY(cache.isKnownHost(subdomain)); + } + + QTest::qWait(lifeTimeMS); + // expired: + QVERIFY(!cache.isKnownHost(exampleCom)); + QVERIFY(!cache.isKnownHost(subdomain)); + + // now check that superdomain's policy expires, but not subdomain's policy: + currentUTC = QDateTime::currentDateTimeUtc(); + cache.updateKnownHost(exampleCom, currentUTC.addMSecs(lifeTimeMS / 5), true); + cache.updateKnownHost(subdomain, currentUTC.addMSecs(lifeTimeMS), true); + QVERIFY(cache.isKnownHost(exampleCom)); + QVERIFY(cache.isKnownHost(subdomain)); + QTest::qWait(lifeTimeMS / 2); + if (QDateTime::currentDateTimeUtc() < currentUTC.addMSecs(lifeTimeMS)) { + QVERIFY(!cache.isKnownHost(exampleCom)); + QVERIFY(cache.isKnownHost(subdomain)); + } +} + +void tst_QHsts::testSTSHeaderParser() +{ + QHstsHeaderParser parser; + using Header = QPair<QByteArray, QByteArray>; + using Headers = QList<Header>; + + QVERIFY(!parser.includeSubDomains()); + QVERIFY(!parser.expirationDate().isValid()); + Headers list; + QVERIFY(!parser.parse(list)); + QVERIFY(!parser.includeSubDomains()); + QVERIFY(!parser.expirationDate().isValid()); + + list << Header("Strict-Transport-security", "200"); + QVERIFY(!parser.parse(list)); + QVERIFY(!parser.includeSubDomains()); + QVERIFY(!parser.expirationDate().isValid()); + + // This header is missing REQUIRED max-age directive, so we'll ignore it: + list << Header("Strict-Transport-Security", "includeSubDomains"); + QVERIFY(!parser.parse(list)); + QVERIFY(!parser.includeSubDomains()); + QVERIFY(!parser.expirationDate().isValid()); + + list.pop_back(); + list << Header("Strict-Transport-Security", "includeSubDomains;max-age=1000"); + QVERIFY(parser.parse(list)); + QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc()); + QVERIFY(parser.includeSubDomains()); + + list.pop_back(); + // Invalid (includeSubDomains twice): + list << Header("Strict-Transport-Security", "max-age = 1000 ; includeSubDomains;includeSubDomains"); + QVERIFY(!parser.parse(list)); + QVERIFY(!parser.includeSubDomains()); + QVERIFY(!parser.expirationDate().isValid()); + + list.pop_back(); + // Invalid (weird number of seconds): + list << Header("Strict-Transport-Security", "max-age=-1000 ; includeSubDomains"); + QVERIFY(!parser.parse(list)); + QVERIFY(!parser.includeSubDomains()); + QVERIFY(!parser.expirationDate().isValid()); + + list.pop_back(); + // Note, directives are case-insensitive + we should ignore unknown directive. + list << Header("Strict-Transport-Security", ";max-age=1000 ;includesubdomains;;" + "nowsomeunknownheader=\"somevaluewithescapes\\;\""); + QVERIFY(parser.parse(list)); + QVERIFY(parser.includeSubDomains()); + QVERIFY(parser.expirationDate().isValid()); + + list.pop_back(); + // Check that we know how to unescape max-age: + list << Header("Strict-Transport-Security", "max-age=\"1000\""); + QVERIFY(parser.parse(list)); + QVERIFY(!parser.includeSubDomains()); + QVERIFY(parser.expirationDate().isValid()); + + list.pop_back(); + // The only STS header, with invalid syntax though, to be ignored: + list << Header("Strict-Transport-Security", "max-age; max-age=15768000"); + QVERIFY(!parser.parse(list)); + QVERIFY(!parser.includeSubDomains()); + QVERIFY(!parser.expirationDate().isValid()); + + // Now we check that our parse chosses the first valid STS header and ignores + // others: + list.clear(); + list << Header("Strict-Transport-Security", "includeSubdomains; max-age=\"hehehe\";"); + list << Header("Strict-Transport-Security", "max-age=10101"); + QVERIFY(parser.parse(list)); + QVERIFY(!parser.includeSubDomains()); + QVERIFY(parser.expirationDate().isValid()); + + + list.clear(); + list << Header("Strict-Transport-Security", "max-age=0"); + QVERIFY(parser.parse(list)); + QVERIFY(!parser.includeSubDomains()); + QVERIFY(parser.expirationDate() <= QDateTime::currentDateTimeUtc()); + + // Parsing is case-insensitive: + list.pop_back(); + list << Header("Strict-Transport-Security", "Max-aGE=1000; InclUdesUbdomains"); + QVERIFY(parser.parse(list)); + QVERIFY(parser.includeSubDomains()); + QVERIFY(parser.expirationDate().isValid()); + + // Grammar of STS header is quite permissive, let's check we can parse + // some weird but valid header: + list.pop_back(); + list << Header("Strict-Transport-Security", ";;; max-age = 17; ; ; ; ;;; ;;" + ";;; ; includeSubdomains ;;thisIsUnknownDirective;;;;"); + QVERIFY(parser.parse(list)); + QVERIFY(parser.includeSubDomains()); + QVERIFY(parser.expirationDate().isValid()); + + list.pop_back(); + list << Header("Strict-Transport-Security", "max-age=1000; includeSubDomains bogon"); + QVERIFY(!parser.parse(list)); + QVERIFY(!parser.includeSubDomains()); + QVERIFY(!parser.expirationDate().isValid()); +} + +QTEST_MAIN(tst_QHsts) + +#include "tst_qhsts.moc" diff --git a/tests/auto/network/access/http2/http2srv.cpp b/tests/auto/network/access/http2/http2srv.cpp index 9d68b5c798..9f77419461 100644 --- a/tests/auto/network/access/http2/http2srv.cpp +++ b/tests/auto/network/access/http2/http2srv.cpp @@ -41,6 +41,7 @@ #include <QtNetwork/qtcpsocket.h> +#include <QtCore/qtimer.h> #include <QtCore/qdebug.h> #include <QtCore/qlist.h> #include <QtCore/qfile.h> @@ -117,6 +118,13 @@ void Http2Server::setResponseBody(const QByteArray &body) responseBody = body; } +void Http2Server::emulateGOAWAY(int timeout) +{ + Q_ASSERT(timeout >= 0); + testingGOAWAY = true; + goawayTimeout = timeout; +} + void Http2Server::startServer() { #ifdef QT_NO_SSL @@ -271,6 +279,16 @@ void Http2Server::connectionEstablished() { using namespace Http2; + if (testingGOAWAY) { + auto timer = new QTimer(this); + timer->setSingleShot(true); + connect(timer, &QTimer::timeout, [this]() { + sendGOAWAY(quint32(connectionStreamID), quint32(INTERNAL_ERROR), 0); + }); + timer->start(goawayTimeout); + return; + } + connect(socket.data(), SIGNAL(readyRead()), this, SLOT(readReady())); diff --git a/tests/auto/network/access/http2/http2srv.h b/tests/auto/network/access/http2/http2srv.h index 15a4f212c9..63a4a4c8e9 100644 --- a/tests/auto/network/access/http2/http2srv.h +++ b/tests/auto/network/access/http2/http2srv.h @@ -70,6 +70,7 @@ public: // To be called before server started: void enablePushPromise(bool enabled, const QByteArray &path = QByteArray()); void setResponseBody(const QByteArray &body); + void emulateGOAWAY(int timeout); // Invokables, since we can call them from the main thread, // but server (can) work on its own thread. @@ -162,6 +163,9 @@ private: quint32 lastPromisedStream = 0; QByteArray pushPath; + bool testingGOAWAY = false; + int goawayTimeout = 0; + protected slots: void ignoreErrorSlot(); }; diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp index 771ddb01be..e7609b3243 100644 --- a/tests/auto/network/access/http2/tst_http2.cpp +++ b/tests/auto/network/access/http2/tst_http2.cpp @@ -69,6 +69,8 @@ private slots: void flowControlClientSide(); void flowControlServerSide(); void pushPromise(); + void goaway_data(); + void goaway(); protected slots: // Slots to listen to our in-process server: @@ -83,6 +85,7 @@ protected slots: void receivedData(quint32 streamID); void windowUpdated(quint32 streamID); void replyFinished(); + void replyFinishedWithError(); private: void clearHTTP2State(); @@ -97,6 +100,7 @@ private: void sendRequest(int streamNumber, QNetworkRequest::Priority priority = QNetworkRequest::NormalPriority, const QByteArray &payload = QByteArray()); + QUrl requestUrl() const; quint16 serverPort = 0; QThread *workerThread = nullptr; @@ -196,9 +200,8 @@ void tst_Http2::singleRequest() QVERIFY(serverPort != 0); - const QString urlAsString(clearTextHTTP2 ? QString("http://127.0.0.1:%1/index.html") - : QString("https://127.0.0.1:%1/index.html")); - const QUrl url(urlAsString.arg(serverPort)); + auto url = requestUrl(); + url.setPath("/index.html"); QNetworkRequest request(url); request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); @@ -347,11 +350,10 @@ void tst_Http2::pushPromise() QVERIFY(serverPort != 0); - const QString urlAsString((clearTextHTTP2 ? QString("http://127.0.0.1:%1/") - : QString("https://127.0.0.1:%1/")).arg(serverPort)); - const QUrl requestUrl(urlAsString + "index.html"); + auto url = requestUrl(); + url.setPath("/index.html"); - QNetworkRequest request(requestUrl); + QNetworkRequest request(url); request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); auto reply = manager.get(request); @@ -374,8 +376,8 @@ void tst_Http2::pushPromise() // Create an additional request (let's say, we parsed reply and realized we // need another resource): - const QUrl promisedUrl(urlAsString + "script.js"); - QNetworkRequest promisedRequest(promisedUrl); + url.setPath("/script.js"); + QNetworkRequest promisedRequest(url); promisedRequest.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); reply = manager.get(promisedRequest); connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); @@ -391,6 +393,61 @@ void tst_Http2::pushPromise() QVERIFY(reply->isFinished()); } +void tst_Http2::goaway_data() +{ + // For now we test only basic things in two very simple scenarios: + // - server sends GOAWAY immediately or + // - server waits for some time (enough for ur to init several streams on a + // client side); then suddenly it replies with GOAWAY, never processing any + // request. + QTest::addColumn<int>("responseTimeoutMS"); + QTest::newRow("ImmediateGOAWAY") << 0; + QTest::newRow("DelayedGOAWAY") << 1000; +} + +void tst_Http2::goaway() +{ + using namespace Http2; + + QFETCH(const int, responseTimeoutMS); + + clearHTTP2State(); + + serverPort = 0; + nRequests = 3; + + ServerPtr srv(newServer(defaultServerSettings, defaultClientSettings)); + srv->emulateGOAWAY(responseTimeoutMS); + QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); + runEventLoop(); + + QVERIFY(serverPort != 0); + + auto url = requestUrl(); + // We have to store these replies, so that we can check errors later. + std::vector<QNetworkReply *> replies(nRequests); + for (int i = 0; i < nRequests; ++i) { + url.setPath(QString("/%1").arg(i)); + QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); + replies[i] = manager.get(request); + QCOMPARE(replies[i]->error(), QNetworkReply::NoError); + void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = + &QNetworkReply::error; + connect(replies[i], errorSignal, this, &tst_Http2::replyFinishedWithError); + // Since we're using self-signed certificates, ignore SSL errors: + replies[i]->ignoreSslErrors(); + } + + runEventLoop(5000 + responseTimeoutMS); + + // No request processed, no 'replyFinished' slot calls: + QCOMPARE(nRequests, 0); + // Our server did not bother to send anything except a single GOAWAY frame: + QVERIFY(!prefaceOK); + QVERIFY(!serverGotSettingsACK); +} + void tst_Http2::serverStarted(quint16 port) { serverPort = port; @@ -445,10 +502,9 @@ void tst_Http2::sendRequest(int streamNumber, QNetworkRequest::Priority priority, const QByteArray &payload) { - static const QString urlAsString(clearTextHTTP2 ? "http://127.0.0.1:%1/stream%2.html" - : "https://127.0.0.1:%1/stream%2.html"); + auto url = requestUrl(); + url.setPath(QString("/stream%1.html").arg(streamNumber)); - const QUrl url(urlAsString.arg(serverPort).arg(streamNumber)); QNetworkRequest request(url); request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); request.setPriority(priority); @@ -463,6 +519,14 @@ void tst_Http2::sendRequest(int streamNumber, connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); } +QUrl tst_Http2::requestUrl() const +{ + static auto url = QUrl(QLatin1String(clearTextHTTP2 ? "http://127.0.0.1" : "https://127.0.0.1")); + url.setPort(serverPort); + + return url; +} + void tst_Http2::clientPrefaceOK() { prefaceOK = true; @@ -476,6 +540,8 @@ void tst_Http2::clientPrefaceError() void tst_Http2::serverSettingsAcked() { serverGotSettingsACK = true; + if (!nRequests) + stopEventLoop(); } void tst_Http2::invalidFrame() @@ -528,6 +594,21 @@ void tst_Http2::replyFinished() QCOMPARE(reply->error(), QNetworkReply::NoError); --nRequests; + if (!nRequests && serverGotSettingsACK) + stopEventLoop(); +} + +void tst_Http2::replyFinishedWithError() +{ + QVERIFY(nRequests); + + if (const auto reply = qobject_cast<QNetworkReply *>(sender())) { + // For now this is a 'generic' code, it just verifies some error was + // reported without testing its type. + QVERIFY(reply->error() != QNetworkReply::NoError); + } + + --nRequests; if (!nRequests) stopEventLoop(); } diff --git a/tests/auto/network/access/qftp/tst_qftp.cpp b/tests/auto/network/access/qftp/tst_qftp.cpp index a13fa86405..a1c8399a26 100644 --- a/tests/auto/network/access/qftp/tst_qftp.cpp +++ b/tests/auto/network/access/qftp/tst_qftp.cpp @@ -124,7 +124,7 @@ protected slots: private: QFtp *newFtp(); void addCommand( QFtp::Command, int ); - bool fileExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &file, const QString &cdDir = QString::null ); + bool fileExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &file, const QString &cdDir = QString() ); bool dirExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &cdDir, const QString &dirToCreate ); void renameInit( const QString &host, const QString &user, const QString &password, const QString &createFile ); diff --git a/tests/auto/network/access/qnetworkreply/test/test.pro b/tests/auto/network/access/qnetworkreply/test/test.pro index 45a5734305..8aeec88fd2 100644 --- a/tests/auto/network/access/qnetworkreply/test/test.pro +++ b/tests/auto/network/access/qnetworkreply/test/test.pro @@ -1,6 +1,6 @@ CONFIG += testcase testcase.timeout = 600 # this test is slow -CONFIG -= app_bundle debug_and_release_target +CONFIG -= debug_and_release_target SOURCES += ../tst_qnetworkreply.cpp TARGET = ../tst_qnetworkreply diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index a7f6a9058a..fbd8f5a780 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -104,10 +104,10 @@ class tst_QNetworkReply: public QObject Q_OBJECT #ifndef QT_NO_NETWORKPROXY - struct ProxyData { + struct ProxyData + { ProxyData(const QNetworkProxy &p, const QByteArray &t, bool auth) - : tag(t), proxy(p), requiresAuthentication(auth) - { } + : tag(t), proxy(p), requiresAuthentication(auth) {} QByteArray tag; QNetworkProxy proxy; bool requiresAuthentication; @@ -115,7 +115,8 @@ class tst_QNetworkReply: public QObject #endif // !QT_NO_NETWORKPROXY static bool seedCreated; - static QString createUniqueExtension() { + static QString createUniqueExtension() + { if (!seedCreated) { qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) + QCoreApplication::applicationPid()); seedCreated = true; // not thread-safe, but who cares @@ -131,7 +132,9 @@ class tst_QNetworkReply: public QObject "location: %1\r\n" "\r\n"; return s; - }; + } + + static const QByteArray httpEmpty200Response; QEventLoop *loop; enum RunSimpleRequestReturn { Timeout = 0, Success, Failure }; @@ -475,6 +478,12 @@ private Q_SLOTS: void ioHttpChangeMaxRedirects(); void ioHttpRedirectErrors_data(); void ioHttpRedirectErrors(); + void ioHttpRedirectPolicy_data(); + void ioHttpRedirectPolicy(); + void ioHttpRedirectPolicyErrors_data(); + void ioHttpRedirectPolicyErrors(); + void ioHttpUserVerifiedRedirect_data(); + void ioHttpUserVerifiedRedirect(); #ifndef QT_NO_SSL void putWithServerClosingConnectionImmediately(); #endif @@ -488,6 +497,8 @@ private: bool notEnoughDataForFastSender; }; +const QByteArray tst_QNetworkReply::httpEmpty200Response = + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; bool tst_QNetworkReply::seedCreated = false; #define RUN_REQUEST(call) \ @@ -495,7 +506,7 @@ bool tst_QNetworkReply::seedCreated = false; QString errorMsg = call; \ if (!errorMsg.isEmpty()) \ QFAIL(qPrintable(errorMsg)); \ - } while (0); + } while (0) #ifndef QT_NO_SSL static void setupSslServer(QSslSocket* serverSocket) @@ -507,6 +518,7 @@ static void setupSslServer(QSslSocket* serverSocket) serverSocket->setProtocol(QSsl::AnyProtocol); serverSocket->setLocalCertificate(testDataDir + "/certs/server.pem"); serverSocket->setPrivateKey(testDataDir + "/certs/server.key"); + serverSocket->startServerEncryption(); } #endif @@ -553,31 +565,30 @@ protected: void incomingConnection(qintptr socketDescriptor) { //qDebug() << "incomingConnection" << socketDescriptor << "doSsl:" << doSsl << "ipv6:" << ipv6; - if (!doSsl) { - client = new QTcpSocket; - client->setSocketDescriptor(socketDescriptor); - connectSocketSignals(); - } else { #ifndef QT_NO_SSL - QSslSocket *serverSocket = new QSslSocket; - serverSocket->setParent(this); - if (serverSocket->setSocketDescriptor(socketDescriptor)) { - connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>))); - setupSslServer(serverSocket); - serverSocket->startServerEncryption(); - client = serverSocket; - connectSocketSignals(); - } else { + if (doSsl) { + QSslSocket *serverSocket = new QSslSocket(this); + if (!serverSocket->setSocketDescriptor(socketDescriptor)) { delete serverSocket; return; } + connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>))); + // connect(serverSocket, &QSslSocket::encrypted, this, &SslServer::ready); ? + setupSslServer(serverSocket); + client = serverSocket; + } else #endif + { + client = new QTcpSocket; + client->setSocketDescriptor(socketDescriptor); } + connectSocketSignals(); client->setParent(this); ++totalConnections; } - virtual void reply() { + virtual void reply() + { Q_ASSERT(!client.isNull()); // we need to emulate the bytesWrittenSlot call if the data is empty. if (dataToTransmit.size() == 0) { @@ -634,7 +645,8 @@ public slots: } } - void bytesWrittenSlot() { + void bytesWrittenSlot() + { Q_ASSERT(!client.isNull()); // Disconnect and delete in next cycle (else Windows clients will fail with RemoteHostClosedError). if (doClose && client->bytesToWrite() == 0) { @@ -879,7 +891,8 @@ class BlockingTcpServer : public QTcpServer public: BlockingTcpServer(bool ssl) : doSsl(ssl), sslSocket(0) {} - QTcpSocket* waitForNextConnectionSocket() { + QTcpSocket* waitForNextConnectionSocket() + { waitForNewConnection(-1); if (doSsl) { if (!sslSocket) @@ -900,7 +913,6 @@ public: serverSocket->setSocketDescriptor(socketDescriptor); connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(slotSslErrors(QList<QSslError>))); setupSslServer(serverSocket); - serverSocket->startServerEncryption(); sslSocket = serverSocket; } else #endif @@ -1381,14 +1393,12 @@ static QByteArray msgWaitForFinished(QNetworkReplyPtr &reply) QString result; QDebug debug(&result); debug << reply->url(); - if (reply->isFinished()) { - if (reply->error() == QNetworkReply::NoError) - debug << "finished."; - else - debug << "failed: #" << reply->error() << reply->errorString(); - } else { + if (!reply->isFinished()) debug << "timed out."; - } + else if (reply->error() == QNetworkReply::NoError) + debug << "finished."; + else + debug << "failed: #" << reply->error() << reply->errorString(); return result.toLocal8Bit(); } @@ -1403,7 +1413,7 @@ int tst_QNetworkReply::waitForFinish(QNetworkReplyPtr &reply) QSignalSpy spy(reply.data(), SIGNAL(downloadProgress(qint64,qint64))); while (!reply->isFinished()) { QTimer::singleShot(5000, loop, SLOT(quit())); - if ( loop->exec() == Timeout && count == spy.count() && !reply->isFinished()) { + if (loop->exec() == Timeout && count == spy.count() && !reply->isFinished()) { returnCode = Timeout; break; } @@ -1417,12 +1427,14 @@ int tst_QNetworkReply::waitForFinish(QNetworkReplyPtr &reply) void tst_QNetworkReply::finished() { - loop->exit(returnCode = Success); + if (loop) + loop->exit(returnCode = Success); } void tst_QNetworkReply::gotError() { - loop->exit(returnCode = Failure); + if (loop) + loop->exit(returnCode = Failure); disconnect(QObject::sender(), SIGNAL(finished()), this, 0); } @@ -4725,11 +4737,13 @@ void tst_QNetworkReply::ioPostToHttpNoBufferFlag() } #ifndef QT_NO_SSL -class SslServer : public QTcpServer { +class SslServer : public QTcpServer +{ Q_OBJECT public: SslServer() : socket(0), m_ssl(true) {} - void incomingConnection(qintptr socketDescriptor) { + void incomingConnection(qintptr socketDescriptor) + { QSslSocket *serverSocket = new QSslSocket; serverSocket->setParent(this); @@ -4739,16 +4753,9 @@ public: emit newPlainConnection(serverSocket); return; } - QString testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath(); - if (testDataDir.isEmpty()) - testDataDir = QCoreApplication::applicationDirPath(); - connect(serverSocket, SIGNAL(encrypted()), this, SLOT(encryptedSlot())); - serverSocket->setProtocol(QSsl::AnyProtocol); connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), serverSocket, SLOT(ignoreSslErrors())); - serverSocket->setLocalCertificate(testDataDir + "/certs/server.pem"); - serverSocket->setPrivateKey(testDataDir + "/certs/server.key"); - serverSocket->startServerEncryption(); + setupSslServer(serverSocket); } else { delete serverSocket; } @@ -4757,11 +4764,13 @@ signals: void newEncryptedConnection(QSslSocket *s); void newPlainConnection(QSslSocket *s); public slots: - void encryptedSlot() { + void encryptedSlot() + { socket = (QSslSocket*) sender(); emit newEncryptedConnection(socket); } - void readyReadSlot() { + void readyReadSlot() + { // for the incoming sockets, not the server socket //qDebug() << static_cast<QSslSocket*>(sender())->bytesAvailable() << static_cast<QSslSocket*>(sender())->encryptedBytesAvailable(); } @@ -4807,7 +4816,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress() disconnect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop())); - incomingSocket->setReadBufferSize(1*1024); + incomingSocket->setReadBufferSize(1024); // some progress should have been made QTRY_VERIFY(!spy.isEmpty()); QList<QVariant> args = spy.last(); @@ -4911,7 +4920,7 @@ void tst_QNetworkReply::ioGetFromBuiltinHttp() QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), (qint64)testData.size()); if (reader.data.size() < testData.size()) { // oops? - QCOMPARE(reader.data, testData.mid(0, reader.data.size())); + QCOMPARE(reader.data, testData.left(reader.data.size())); qDebug() << "The data is incomplete, the last" << testData.size() - reader.data.size() << "bytes are missing"; } QCOMPARE(reader.data.size(), testData.size()); @@ -4958,7 +4967,7 @@ void tst_QNetworkReply::ioPostToHttpUploadProgress() QVERIFY(incomingSocket); disconnect(&server, SIGNAL(newConnection()), &QTestEventLoop::instance(), SLOT(exitLoop())); - incomingSocket->setReadBufferSize(1*1024); + incomingSocket->setReadBufferSize(1024); QTestEventLoop::instance().enterLoop(5); // some progress should have been made QVERIFY(!spy.isEmpty()); @@ -5660,12 +5669,14 @@ void tst_QNetworkReply::httpProxyCommands() QCOMPARE(uaheader, QByteArray("User-Agent: QNetworkReplyAutoTest/1.0")); } -class ProxyChangeHelper : public QObject { +class ProxyChangeHelper : public QObject +{ Q_OBJECT public: ProxyChangeHelper() : QObject(), signalCount(0) {}; public slots: - void finishedSlot() { + void finishedSlot() + { signalCount++; if (signalCount == 2) QMetaObject::invokeMethod(&QTestEventLoop::instance(), "exitLoop", Qt::QueuedConnection); @@ -5911,7 +5922,8 @@ void tst_QNetworkReply::httpReUsingConnectionSequential() reply2->deleteLater(); } -class HttpReUsingConnectionFromFinishedSlot : public QObject { +class HttpReUsingConnectionFromFinishedSlot : public QObject +{ Q_OBJECT public: QNetworkReply* reply1; @@ -5919,7 +5931,8 @@ public: QUrl url; QNetworkAccessManager manager; public slots: - void finishedSlot() { + void finishedSlot() + { QVERIFY(!reply1->error()); QFETCH(bool, doDeleteLater); @@ -5967,7 +5980,8 @@ void tst_QNetworkReply::httpReUsingConnectionFromFinishedSlot() QCOMPARE(server.totalConnections, 1); } -class HttpRecursiveCreationHelper : public QObject { +class HttpRecursiveCreationHelper : public QObject +{ Q_OBJECT public: @@ -5983,7 +5997,8 @@ public: int requestsStartedCount_readyRead; int requestsFinishedCount; public slots: - void finishedSlot() { + void finishedSlot() + { requestsFinishedCount++; QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); @@ -6002,7 +6017,8 @@ public slots: reply->deleteLater(); } - void readyReadSlot() { + void readyReadSlot() + { QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); QVERIFY(!reply->error()); @@ -6011,7 +6027,8 @@ public slots: requestsStartedCount_readyRead++; } } - void startOne() { + void startOne() + { QUrl url = "http://" + QtNetworkSettings::serverName() + "/qtest/fluke.gif"; QNetworkRequest request(url); QNetworkReply *reply = manager.get(request); @@ -6380,7 +6397,8 @@ void tst_QNetworkReply::getFromHttpIntoBuffer() } // FIXME we really need to consolidate all those server implementations -class GetFromHttpIntoBuffer2Server : QObject { +class GetFromHttpIntoBuffer2Server : QObject +{ Q_OBJECT qint64 dataSize; qint64 dataSent; @@ -6390,26 +6408,28 @@ class GetFromHttpIntoBuffer2Server : QObject { bool chunkedEncoding; public: - GetFromHttpIntoBuffer2Server (qint64 ds, bool sscl, bool ce) : dataSize(ds), dataSent(0), - client(0), serverSendsContentLength(sscl), chunkedEncoding(ce) { + GetFromHttpIntoBuffer2Server (qint64 ds, bool sscl, bool ce) + : dataSize(ds), dataSent(0), client(0), + serverSendsContentLength(sscl), chunkedEncoding(ce) + { server.listen(); connect(&server, SIGNAL(newConnection()), this, SLOT(newConnectionSlot())); } - int serverPort() { - return server.serverPort(); - } + int serverPort() { return server.serverPort(); } public slots: - void newConnectionSlot() { + void newConnectionSlot() + { client = server.nextPendingConnection(); client->setParent(this); connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot(qint64))); } - void readyReadSlot() { + void readyReadSlot() + { client->readAll(); client->write("HTTP/1.0 200 OK\n"); if (serverSendsContentLength) @@ -6419,7 +6439,8 @@ public slots: client->write("Connection: close\n\n"); } - void bytesWrittenSlot(qint64 amount) { + void bytesWrittenSlot(qint64 amount) + { Q_UNUSED(amount); if (dataSent == dataSize && client) { // close eventually @@ -6453,7 +6474,8 @@ public slots: } }; -class GetFromHttpIntoBuffer2Client : QObject { +class GetFromHttpIntoBuffer2Client : QObject +{ Q_OBJECT private: bool useDownloadBuffer; @@ -6470,7 +6492,8 @@ public: } public slots: - void metaDataChangedSlot() { + void metaDataChangedSlot() + { if (useDownloadBuffer) { QSharedPointer<char> sharedPointer = qvariant_cast<QSharedPointer<char> >(reply->attribute(QNetworkRequest::DownloadBufferAttribute)); QVERIFY(!sharedPointer.isNull()); // It will be 0 if it failed @@ -6480,7 +6503,8 @@ public: QVERIFY(bytesAvailableList.isEmpty()); } - void readyReadSlot() { + void readyReadSlot() + { QVERIFY(!reply->isFinished()); qint64 bytesAvailable = reply->bytesAvailable(); @@ -6502,7 +6526,8 @@ public: // Add bytesAvailable to a list an parse } - void finishedSlot() { + void finishedSlot() + { // We should have already received all readyRead QVERIFY(!bytesAvailableList.isEmpty()); QCOMPARE(bytesAvailableList.last(), uploadSize); @@ -6797,7 +6822,8 @@ void tst_QNetworkReply::authenticationCacheAfterCancel() // QTBUG-23136 workaround (needed even with danted v1.1.19): if (proxy.port() == 1081) { #ifdef QT_BUILD_INTERNAL - QNetworkAccessManagerPrivate::clearCache(&manager); + QNetworkAccessManagerPrivate::clearAuthenticationCache(&manager); + QNetworkAccessManagerPrivate::clearConnectionCache(&manager); #else return; #endif @@ -6908,17 +6934,17 @@ void tst_QNetworkReply::authenticationWithDifferentRealm() } #endif // !QT_NO_NETWORKPROXY -class QtBug13431Helper : public QObject { +class QtBug13431Helper : public QObject +{ Q_OBJECT public: QNetworkReply* m_reply; QTimer m_dlTimer; public slots: - void replyFinished(QNetworkReply*) { - QTestEventLoop::instance().exitLoop(); - } + void replyFinished(QNetworkReply*) { QTestEventLoop::instance().exitLoop(); } - void onReadAndReschedule() { + void onReadAndReschedule() + { const qint64 bytesReceived = m_reply->bytesAvailable(); if (bytesReceived && m_reply->readBufferSize()) { QByteArray data = m_reply->read(bytesReceived); @@ -7066,7 +7092,8 @@ void tst_QNetworkReply::qtbug22660gzipNoContentLengthEmptyContent() QCOMPARE(reply->readAll(), QByteArray()); } -class QtBug27161Helper : public QObject { +class QtBug27161Helper : public QObject +{ Q_OBJECT public: QtBug27161Helper(MiniHttpServer & server, const QByteArray & data): @@ -7076,16 +7103,19 @@ public: connect(&m_server, SIGNAL(newConnection()), this, SLOT(newConnectionSlot())); } public slots: - void newConnectionSlot(){ + void newConnectionSlot() + { connect(m_server.client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot())); } - void bytesWrittenSlot(){ + void bytesWrittenSlot() + { disconnect(m_server.client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot())); m_Timer.singleShot(100, this, SLOT(timeoutSlot())); } - void timeoutSlot(){ + void timeoutSlot() + { m_server.doClose = true; // we need to emulate the bytesWrittenSlot call if the data is empty. if (m_data.size() == 0) @@ -7514,10 +7544,12 @@ void tst_QNetworkReply::httpUserAgent() void tst_QNetworkReply::synchronousAuthenticationCache() { - class MiniAuthServer : public MiniHttpServer { + class MiniAuthServer : public MiniHttpServer + { public: - MiniAuthServer(QThread *thread) : MiniHttpServer(QByteArray(), false, thread) {}; - virtual void reply() { + MiniAuthServer(QThread *thread) : MiniHttpServer(QByteArray(), false, thread) {} + virtual void reply() + { dataToTransmit = "HTTP/1.0 401 Unauthorized\r\n" @@ -7929,7 +7961,8 @@ public: qint64 bandwidthQuota; QTimer timer; - RateLimitedUploadDevice(QByteArray d) : QIODevice(),data(d),read(0),bandwidthQuota(0) { + RateLimitedUploadDevice(QByteArray d) : QIODevice(),data(d),read(0),bandwidthQuota(0) + { buffer.setData(data); buffer.open(QIODevice::ReadOnly); timer.setInterval(200); @@ -7937,12 +7970,14 @@ public: timer.start(); } - virtual qint64 writeData(const char* , qint64 ) { + virtual qint64 writeData(const char* , qint64 ) + { Q_ASSERT(false); return 0; } - virtual qint64 readData(char* data, qint64 maxlen) { + virtual qint64 readData(char* data, qint64 maxlen) + { //qDebug() << Q_FUNC_INFO << maxlen << bandwidthQuota; maxlen = qMin(maxlen, buffer.bytesAvailable()); maxlen = qMin(maxlen, bandwidthQuota); @@ -7959,24 +7994,17 @@ public: //qDebug() << Q_FUNC_INFO << maxlen << bandwidthQuota << read << ret << buffer.bytesAvailable(); return ret; } - virtual bool atEnd() const { - return buffer.atEnd(); - } - virtual qint64 size() const{ - return data.length(); - } + virtual bool atEnd() const { return buffer.atEnd(); } + virtual qint64 size() const { return data.length(); } qint64 bytesAvailable() const { return buffer.bytesAvailable() + QIODevice::bytesAvailable(); } - virtual bool isSequential() const{ // random access, we can seek - return false; - } - virtual bool seek ( qint64 pos ) { - return buffer.seek(pos); - } + virtual bool isSequential() const { return false; } // random access, we can seek + virtual bool seek (qint64 pos) { return buffer.seek(pos); } protected slots: - void timeoutSlot() { + void timeoutSlot() + { //qDebug() << Q_FUNC_INFO; bandwidthQuota = 8*1024; // fill quota emit readyRead(); @@ -8012,10 +8040,9 @@ void tst_QNetworkReply::putWithRateLimiting() void tst_QNetworkReply::ioHttpSingleRedirect() { QUrl localhost = QUrl("http://localhost"); - QByteArray http200Reply = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; // Setup server to which the second server will redirect to - MiniHttpServer server2(http200Reply); + MiniHttpServer server2(httpEmpty200Response); QUrl redirectUrl = QUrl(localhost); redirectUrl.setPort(server2.serverPort()); @@ -8057,11 +8084,9 @@ void tst_QNetworkReply::ioHttpChangeMaxRedirects() { QUrl localhost = QUrl("http://localhost"); - QByteArray http200Reply = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; - MiniHttpServer server1(""); MiniHttpServer server2(""); - MiniHttpServer server3(http200Reply); + MiniHttpServer server3(httpEmpty200Response); QUrl server2Url(localhost); server2Url.setPort(server2.serverPort()); @@ -8145,6 +8170,221 @@ void tst_QNetworkReply::ioHttpRedirectErrors() QCOMPARE(spy.count(), 1); QCOMPARE(reply->error(), error); } + +struct SameOriginRedirector : MiniHttpServer +{ + SameOriginRedirector(const QByteArray &data, bool ssl = false) + : MiniHttpServer(data, ssl) + { } + + std::vector<QByteArray> responses; + + void reply() override + { + if (responses.empty()) { + dataToTransmit.clear(); + } else { + dataToTransmit = responses.back(); + responses.pop_back(); + } + + MiniHttpServer::reply(); + } +}; + +void tst_QNetworkReply::ioHttpRedirectPolicy_data() +{ + QTest::addColumn<QNetworkRequest::RedirectPolicy>("policy"); + QTest::addColumn<bool>("ssl"); + QTest::addColumn<int>("redirectCount"); + QTest::addColumn<int>("statusCode"); + + QTest::newRow("manual-nossl") << QNetworkRequest::ManualRedirectPolicy << false << 0 << 307; + QTest::newRow("manual-ssl") << QNetworkRequest::ManualRedirectPolicy << true << 0 << 307; + QTest::newRow("nolesssafe-nossl") << QNetworkRequest::NoLessSafeRedirectPolicy << false << 1 << 200; + QTest::newRow("nolesssafe-ssl") << QNetworkRequest::NoLessSafeRedirectPolicy << true << 1 << 200; + QTest::newRow("same-origin-nossl") << QNetworkRequest::SameOriginRedirectPolicy << false << 1 << 200; + QTest::newRow("same-origin-ssl") << QNetworkRequest::SameOriginRedirectPolicy << true << 1 << 200; +} + +void tst_QNetworkReply::ioHttpRedirectPolicy() +{ + QFETCH(const QNetworkRequest::RedirectPolicy, policy); + + QFETCH(const bool, ssl); +#ifdef QT_NO_SSL + if (ssl) + QSKIP("SSL is not supported"); +#endif + + QFETCH(const int, redirectCount); + QFETCH(const int, statusCode); + + // Setup HTTP server. + SameOriginRedirector redirectServer("", ssl); + + QUrl url(QLatin1String( +#ifndef QT_NO_SSL + ssl ? "https://localhost" : +#endif + "http://localhost")); + + url.setPort(redirectServer.serverPort()); + redirectServer.responses.push_back(httpEmpty200Response); + redirectServer.responses.push_back(tempRedirectReplyStr().arg(QString(url.toEncoded())).toLatin1()); + + // This is the default one we preserve between tests. + QCOMPARE(manager.redirectPolicy(), QNetworkRequest::ManualRedirectPolicy); + + manager.setRedirectPolicy(policy); + QCOMPARE(manager.redirectPolicy(), policy); + QNetworkReplyPtr reply(manager.get(QNetworkRequest(url))); + if (ssl) + reply->ignoreSslErrors(); + + // Restore default: + manager.setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); + QSignalSpy redirectSpy(reply.data(), SIGNAL(redirected(QUrl))); + QSignalSpy finishedSpy(reply.data(), SIGNAL(finished())); + QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(redirectSpy.count(), redirectCount); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode); +} + +void tst_QNetworkReply::ioHttpRedirectPolicyErrors_data() +{ + QTest::addColumn<QNetworkRequest::RedirectPolicy>("policy"); + QTest::addColumn<bool>("ssl"); + QTest::addColumn<QString>("location"); + QTest::addColumn<int>("maxRedirects"); + QTest::addColumn<QNetworkReply::NetworkError>("expectedError"); + + // 1. NoLessSafeRedirectsPolicy + QTest::newRow("nolesssafe-nossl-nossl-too-many") << QNetworkRequest::NoLessSafeRedirectPolicy + << false << QString("http://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError; + QTest::newRow("nolesssafe-ssl-ssl-too-many") << QNetworkRequest::NoLessSafeRedirectPolicy + << true << QString("https:/localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError; + QTest::newRow("nolesssafe-ssl-nossl-insecure-redirect") << QNetworkRequest::NoLessSafeRedirectPolicy + << true << QString("http://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError; + // 2. SameOriginRedirectsPolicy + QTest::newRow("same-origin-nossl-nossl-too-many") << QNetworkRequest::SameOriginRedirectPolicy + << false << QString("http://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError; + QTest::newRow("same-origin-ssl-ssl-too-many") << QNetworkRequest::SameOriginRedirectPolicy + << true << QString("https://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError; + QTest::newRow("same-origin-https-http-wrong-protocol") << QNetworkRequest::SameOriginRedirectPolicy + << true << QString("http://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError; + QTest::newRow("same-origin-http-https-wrong-protocol") << QNetworkRequest::SameOriginRedirectPolicy + << false << QString("https://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError; + QTest::newRow("same-origin-http-http-wrong-host") << QNetworkRequest::SameOriginRedirectPolicy + << false << QString("http://not-so-localhost:%1") << 50 << QNetworkReply::InsecureRedirectError; + QTest::newRow("same-origin-https-https-wrong-host") << QNetworkRequest::SameOriginRedirectPolicy + << true << QString("https://not-so-localhost:%1") << 50 << QNetworkReply::InsecureRedirectError; + QTest::newRow("same-origin-http-http-wrong-port") << QNetworkRequest::SameOriginRedirectPolicy + << false << QString("http://localhost/%1") << 50 << QNetworkReply::InsecureRedirectError; + QTest::newRow("same-origin-https-https-wrong-port") << QNetworkRequest::SameOriginRedirectPolicy + << true << QString("https://localhost/%1") << 50 << QNetworkReply::InsecureRedirectError; +} + +void tst_QNetworkReply::ioHttpRedirectPolicyErrors() +{ + QFETCH(const QNetworkRequest::RedirectPolicy, policy); + // This should never happen: + QVERIFY(policy != QNetworkRequest::ManualRedirectPolicy); + + QFETCH(const bool, ssl); + QFETCH(const QString, location); + QFETCH(const int, maxRedirects); + QFETCH(const QNetworkReply::NetworkError, expectedError); + +#ifdef QT_NO_SSL + if (ssl || location.contains("https")) + QSKIP("SSL required to run this test"); +#endif + + // Setup the server. + MiniHttpServer server("", ssl); + server.setDataToTransmit(tempRedirectReplyStr().arg(location.arg(server.serverPort())).toLatin1()); + + QUrl url(QLatin1String( +#ifndef QT_NO_SSL + ssl ? "https://localhost" : +#endif + "http://localhost")); + url.setPort(server.serverPort()); + + QNetworkRequest request(url); + request.setMaximumRedirectsAllowed(maxRedirects); + // We always reset the policy to the default one ('Manual') after any related + // test is finished: + QCOMPARE(manager.redirectPolicy(), QNetworkRequest::ManualRedirectPolicy); + manager.setRedirectPolicy(policy); + QCOMPARE(manager.redirectPolicy(), policy); + + QNetworkReplyPtr reply(manager.get(request)); + // Set it back to default: + manager.setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); + + if (ssl) + reply->ignoreSslErrors(); + + QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QCOMPARE(waitForFinish(reply), int(Failure)); + QCOMPARE(spy.count(), 1); + QCOMPARE(reply->error(), expectedError); +} + +void tst_QNetworkReply::ioHttpUserVerifiedRedirect_data() +{ + QTest::addColumn<bool>("followRedirect"); + QTest::addColumn<int>("statusCode"); + + QTest::newRow("allow-redirect") << true << 200; + QTest::newRow("reject-redirect") << false << 307; +} + +void tst_QNetworkReply::ioHttpUserVerifiedRedirect() +{ + QFETCH(const bool, followRedirect); + QFETCH(const int, statusCode); + + // Setup HTTP server. + MiniHttpServer target(httpEmpty200Response, false); + QUrl url("http://localhost"); + url.setPort(target.serverPort()); + + MiniHttpServer redirectServer("", false); + redirectServer.setDataToTransmit(tempRedirectReplyStr().arg(QString(url.toEncoded())).toLatin1()); + url.setPort(redirectServer.serverPort()); + + QCOMPARE(manager.redirectPolicy(), QNetworkRequest::ManualRedirectPolicy); + manager.setRedirectPolicy(QNetworkRequest::UserVerifiedRedirectPolicy); + QCOMPARE(manager.redirectPolicy(), QNetworkRequest::UserVerifiedRedirectPolicy); + + QNetworkReplyPtr reply(manager.get(QNetworkRequest(url))); + reply->connect(reply.data(), &QNetworkReply::redirected, + [&](const QUrl &redirectUrl) { + qDebug() << "redirect to:" << redirectUrl; + if (followRedirect) { + qDebug() << "confirmed."; + emit reply->redirectAllowed(); + } else{ + qDebug() << "rejected."; + emit reply->abort(); + } + }); + + // Before any test failed, reset the policy to default: + manager.setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); + QCOMPARE(manager.redirectPolicy(), QNetworkRequest::ManualRedirectPolicy); + + QSignalSpy finishedSpy(reply.data(), SIGNAL(finished())); + waitForFinish(reply); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode); +} + #ifndef QT_NO_SSL class PutWithServerClosingConnectionImmediatelyHandler: public QObject @@ -8166,9 +8406,7 @@ signals: void corruptFileUploadReceived(); public slots: - void closeDelayed() { - m_socket->close(); - } + void closeDelayed() { m_socket->close(); } void readyReadSlot() { @@ -8193,17 +8431,18 @@ public slots: // We had received some data but it is corrupt! qDebug() << "CORRUPT" << m_receivedData.count(); - // Use this to track down the pattern of the corruption and conclude the source -// QFile a("/tmp/corrupt"); -// a.open(QIODevice::WriteOnly); -// a.write(m_receivedData); -// a.close(); +#if 0 // Use this to track down the pattern of the corruption and conclude the source + QFile a("/tmp/corrupt"); + a.open(QIODevice::WriteOnly); + a.write(m_receivedData); + a.close(); -// QFile b("/tmp/correct"); -// b.open(QIODevice::WriteOnly); -// b.write(m_expectedData); -// b.close(); + QFile b("/tmp/correct"); + b.open(QIODevice::WriteOnly); + b.write(m_expectedData); + b.close(); //exit(1); +#endif emit corruptFileUploadReceived(); } else { emit correctFileUploadReceived(); @@ -8220,26 +8459,26 @@ public: int m_repliesFinished; int m_expectedReplies; QByteArray m_expectedData; - PutWithServerClosingConnectionImmediatelyServer() : SslServer(), m_correctUploads(0), m_corruptUploads(0), m_repliesFinished(0), m_expectedReplies(0) + PutWithServerClosingConnectionImmediatelyServer() + : SslServer(), m_correctUploads(0), m_corruptUploads(0), + m_repliesFinished(0), m_expectedReplies(0) { QObject::connect(this, SIGNAL(newEncryptedConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*))); QObject::connect(this, SIGNAL(newPlainConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*))); } public slots: - void createHandlerForConnection(QSslSocket* s) { + void createHandlerForConnection(QSslSocket* s) + { PutWithServerClosingConnectionImmediatelyHandler *handler = new PutWithServerClosingConnectionImmediatelyHandler(s, m_expectedData); handler->setParent(this); QObject::connect(handler, SIGNAL(correctFileUploadReceived()), this, SLOT(increaseCorrect())); QObject::connect(handler, SIGNAL(corruptFileUploadReceived()), this, SLOT(increaseCorrupt())); } - void increaseCorrect() { - m_correctUploads++; - } - void increaseCorrupt() { - m_corruptUploads++; - } - void replyFinished() { + void increaseCorrect() { m_correctUploads++; } + void increaseCorrupt() { m_corruptUploads++; } + void replyFinished() + { m_repliesFinished++; if (m_repliesFinished == m_expectedReplies) { QTestEventLoop::instance().exitLoop(); diff --git a/tests/auto/network/bearer/qnetworkconfiguration/tst_qnetworkconfiguration.cpp b/tests/auto/network/bearer/qnetworkconfiguration/tst_qnetworkconfiguration.cpp index 82fa5cab9c..9c49e0c173 100644 --- a/tests/auto/network/bearer/qnetworkconfiguration/tst_qnetworkconfiguration.cpp +++ b/tests/auto/network/bearer/qnetworkconfiguration/tst_qnetworkconfiguration.cpp @@ -53,6 +53,7 @@ private slots: void comparison(); void children(); void isRoamingAvailable(); + void connectTimeout(); #endif }; @@ -181,6 +182,21 @@ void tst_QNetworkConfiguration::isRoamingAvailable() } } } + +void tst_QNetworkConfiguration::connectTimeout() +{ + QNetworkConfigurationManager manager; + QList<QNetworkConfiguration> configs = manager.allConfigurations(); + + foreach (QNetworkConfiguration networkConfiguration, configs) { + QCOMPARE(networkConfiguration.connectTimeout(), 30000); + + bool result = networkConfiguration.setConnectTimeout(100); + QVERIFY(result); + + QCOMPARE(networkConfiguration.connectTimeout(), 100); + } +} #endif QTEST_MAIN(tst_QNetworkConfiguration) diff --git a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp index a715c38f32..bc3f5650ba 100644 --- a/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp +++ b/tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp @@ -274,6 +274,7 @@ void tst_QHostAddress::specialAddresses() QVERIFY(address == QHostAddress(address)); QVERIFY(!(QHostAddress(address) != QHostAddress(address))); QVERIFY(!(QHostAddress(address) != address)); + QVERIFY(!(address != QHostAddress(address))); { QHostAddress ha; diff --git a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp index 0c01657956..2671c253cb 100644 --- a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp +++ b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp @@ -92,6 +92,10 @@ private slots: void lookupIPv4(); void lookupIPv6_data(); void lookupIPv6(); + void lookupConnectToFunctionPointer_data(); + void lookupConnectToFunctionPointer(); + void lookupConnectToLambda_data(); + void lookupConnectToLambda(); void reverseLookup_data(); void reverseLookup(); @@ -306,6 +310,74 @@ void tst_QHostInfo::lookupIPv6() QCOMPARE(tmp.join(' ').toLower(), expected.join(' ').toLower()); } +void tst_QHostInfo::lookupConnectToFunctionPointer_data() +{ + lookupIPv4_data(); +} + +void tst_QHostInfo::lookupConnectToFunctionPointer() +{ + QFETCH(QString, hostname); + QFETCH(int, err); + QFETCH(QString, addresses); + + lookupDone = false; + QHostInfo::lookupHost(hostname, this, &tst_QHostInfo::resultsReady); + + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(lookupDone); + + if (int(lookupResults.error()) != int(err)) + qWarning() << hostname << "=>" << lookupResults.errorString(); + QCOMPARE(int(lookupResults.error()), int(err)); + + QStringList tmp; + for (const auto &result : lookupResults.addresses()) + tmp.append(result.toString()); + tmp.sort(); + + QStringList expected = addresses.split(' '); + expected.sort(); + + QCOMPARE(tmp.join(' '), expected.join(' ')); +} + +void tst_QHostInfo::lookupConnectToLambda_data() +{ + lookupIPv4_data(); +} + +void tst_QHostInfo::lookupConnectToLambda() +{ + QFETCH(QString, hostname); + QFETCH(int, err); + QFETCH(QString, addresses); + + lookupDone = false; + QHostInfo::lookupHost(hostname, [=](const QHostInfo &hostInfo) { + resultsReady(hostInfo); + }); + + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(lookupDone); + + if (int(lookupResults.error()) != int(err)) + qWarning() << hostname << "=>" << lookupResults.errorString(); + QCOMPARE(int(lookupResults.error()), int(err)); + + QStringList tmp; + for (int i = 0; i < lookupResults.addresses().count(); ++i) + tmp.append(lookupResults.addresses().at(i).toString()); + tmp.sort(); + + QStringList expected = addresses.split(' '); + expected.sort(); + + QCOMPARE(tmp.join(' '), expected.join(' ')); +} + void tst_QHostInfo::reverseLookup_data() { QTest::addColumn<QString>("address"); diff --git a/tests/auto/network/kernel/qnetworkdatagram/qnetworkdatagram.pro b/tests/auto/network/kernel/qnetworkdatagram/qnetworkdatagram.pro index a2fe44060e..23d57f3fbf 100644 --- a/tests/auto/network/kernel/qnetworkdatagram/qnetworkdatagram.pro +++ b/tests/auto/network/kernel/qnetworkdatagram/qnetworkdatagram.pro @@ -1,5 +1,4 @@ CONFIG += testcase console -CONFIG -= app_bundle TARGET = tst_qnetworkdatagram SOURCES += tst_qnetworkdatagram.cpp QT = core network testlib diff --git a/tests/auto/network/kernel/qnetworkdatagram/tst_qnetworkdatagram.cpp b/tests/auto/network/kernel/qnetworkdatagram/tst_qnetworkdatagram.cpp index 3295580432..5eedd1043b 100644 --- a/tests/auto/network/kernel/qnetworkdatagram/tst_qnetworkdatagram.cpp +++ b/tests/auto/network/kernel/qnetworkdatagram/tst_qnetworkdatagram.cpp @@ -1,9 +1,9 @@ /**************************************************************************** ** ** Copyright (C) 2016 Intel Corporation. -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** -** This file is part of the QtNetwork module of the Qt Toolkit. +** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage diff --git a/tests/auto/network/socket/qsctpsocket/tst_qsctpsocket.cpp b/tests/auto/network/socket/qsctpsocket/tst_qsctpsocket.cpp index cf15e60531..89a1430948 100644 --- a/tests/auto/network/socket/qsctpsocket/tst_qsctpsocket.cpp +++ b/tests/auto/network/socket/qsctpsocket/tst_qsctpsocket.cpp @@ -1,31 +1,26 @@ /**************************************************************************** ** ** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com> -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/tests/auto/network/ssl/qssldiffiehellmanparameters/tst_qssldiffiehellmanparameters.cpp b/tests/auto/network/ssl/qssldiffiehellmanparameters/tst_qssldiffiehellmanparameters.cpp index ddf503eed6..60add4a51c 100644 --- a/tests/auto/network/ssl/qssldiffiehellmanparameters/tst_qssldiffiehellmanparameters.cpp +++ b/tests/auto/network/ssl/qssldiffiehellmanparameters/tst_qssldiffiehellmanparameters.cpp @@ -1,11 +1,11 @@ /**************************************************************************** ** ** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk> -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** -** This file is part of the QtNetwork module of the Qt Toolkit. +** This file is part of the test suite of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the @@ -14,24 +14,13 @@ ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. +** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** |