summaryrefslogtreecommitdiffstats
path: root/tests/auto/network/access
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/network/access')
-rw-r--r--tests/auto/network/access/access.pro6
-rw-r--r--tests/auto/network/access/hsts/hsts.pro6
-rw-r--r--tests/auto/network/access/hsts/tst_qhsts.cpp318
-rw-r--r--tests/auto/network/access/http2/http2srv.cpp18
-rw-r--r--tests/auto/network/access/http2/http2srv.h4
-rw-r--r--tests/auto/network/access/http2/tst_http2.cpp105
-rw-r--r--tests/auto/network/access/qftp/tst_qftp.cpp2
-rw-r--r--tests/auto/network/access/qnetworkreply/test/test.pro2
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp489
9 files changed, 809 insertions, 141 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();