diff options
Diffstat (limited to 'tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp')
-rw-r--r-- | tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp | 1135 |
1 files changed, 844 insertions, 291 deletions
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index 78a3bb51aa..a4a05b18f5 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtNetwork/qtnetworkglobal.h> @@ -16,20 +16,23 @@ #include <QBuffer> #include <QMap> -#include <QtCore/qlist.h> -#include <QtCore/qset.h> #include <QtCore/QCryptographicHash> #include <QtCore/QDataStream> -#include <QtCore/QUrl> +#include <QtCore/QDateTime> #include <QtCore/QEventLoop> #include <QtCore/QElapsedTimer> #include <QtCore/QFile> +#include <QtCore/QList> #include <QtCore/QRandomGenerator> #include <QtCore/QRegularExpression> #include <QtCore/QRegularExpressionMatch> +#include <QtCore/QSet> #include <QtCore/QSharedPointer> #include <QtCore/QScopedPointer> #include <QtCore/QTemporaryFile> +#include <QtCore/QTimeZone> +#include <QtCore/QUrl> + #include <QtNetwork/QTcpServer> #include <QtNetwork/QTcpSocket> #include <QtNetwork/QLocalSocket> @@ -44,6 +47,7 @@ #include <QtNetwork/qnetworkdiskcache.h> #include <QtNetwork/qnetworkrequest.h> #include <QtNetwork/qnetworkreply.h> +#include <QtNetwork/QHttp1Configuration> #include <QtNetwork/qnetworkcookie.h> #include <QtNetwork/QNetworkCookieJar> #include <QtNetwork/QHttpPart> @@ -64,6 +68,9 @@ Q_DECLARE_METATYPE(QSharedPointer<char>) #endif +#include <memory> +#include <optional> + #ifdef Q_OS_UNIX # include <sys/types.h> # include <unistd.h> // for getuid() @@ -84,6 +91,7 @@ Q_DECLARE_METATYPE(QNetworkProxyQuery) typedef QSharedPointer<QNetworkReply> QNetworkReplyPtr; using namespace Qt::StringLiterals; +using namespace std::chrono_literals; #if QT_CONFIG(ssl) QT_BEGIN_NAMESPACE @@ -126,6 +134,29 @@ class tst_QNetworkReply: public QObject "\r\n"; return s; } + static QString movedReplyStr() { + QString s = "HTTP/1.1 301 Moved Permanently\r\n" + "Content-Type: text/plain\r\n" + "location: %1\r\n" + "\r\n"; + return s; + } + + static QString foundReplyStr() { + QString s = "HTTP/1.1 302 Found\r\n" + "Content-Type: text/plain\r\n" + "location: %1\r\n" + "\r\n"; + return s; + } + + static QString permRedirectReplyStr() { + QString s = "HTTP/1.1 308 Permanent Redirect\r\n" + "Content-Type: text/plain\r\n" + "location: %1\r\n" + "\r\n"; + return s; + } static const QByteArray httpEmpty200Response; static const QString filePermissionFileName; @@ -209,6 +240,12 @@ private Q_SLOTS: void getFromFtpAfterError(); // QTBUG-40797 void getFromHttp_data(); void getFromHttp(); + void getWithBodyFromHttp_data(); + void getWithBodyFromHttp(); + void getWithAndWithoutBodyFromHttp_data(); + void getWithAndWithoutBodyFromHttp(); + void getWithBodyRedirected_data(); + void getWithBodyRedirected(); void getErrors_data(); void getErrors(); #if QT_CONFIG(networkproxy) @@ -226,6 +263,8 @@ private Q_SLOTS: void putToHttpSynchronous(); void putToHttpMultipart_data(); void putToHttpMultipart(); + void putWithoutBody(); + void putWithoutBody_data(); void postToHttp_data(); void postToHttp(); void postToHttpSynchronous_data(); @@ -233,6 +272,8 @@ private Q_SLOTS: void postToHttpMultipart_data(); void postToHttpMultipart(); void multipartSkipIndices(); // QTBUG-32534 + void postWithoutBody_data(); + void postWithoutBody(); #if QT_CONFIG(ssl) void putToHttps_data(); void putToHttps(); @@ -285,8 +326,8 @@ private Q_SLOTS: #endif void ioGetFromHttpBrokenServer_data(); void ioGetFromHttpBrokenServer(); - void ioGetFromHttpStatus100_data(); - void ioGetFromHttpStatus100(); + void ioGetFromHttpStatusInformational_data(); + void ioGetFromHttpStatusInformational(); void ioGetFromHttpNoHeaders_data(); void ioGetFromHttpNoHeaders(); void ioGetFromHttpWithCache_data(); @@ -443,6 +484,9 @@ private Q_SLOTS: void varyingCacheExpiry_data(); void varyingCacheExpiry(); + void amountOfHttp1ConnectionsQtbug25280_data(); + void amountOfHttp1ConnectionsQtbug25280(); + void dontInsertPartialContentIntoTheCache(); void httpUserAgent(); @@ -496,8 +540,8 @@ private Q_SLOTS: void autoDeleteReplies_data(); void autoDeleteReplies(); - void getWithTimeout(); - void postWithTimeout(); + void requestWithTimeout_data(); + void requestWithTimeout(); void moreActivitySignals_data(); void moreActivitySignals(); @@ -516,6 +560,11 @@ private Q_SLOTS: void notFoundWithCompression_data(); void notFoundWithCompression(); + void qtbug68821proxyError_data(); + void qtbug68821proxyError(); + + void abortAndError(); + // NOTE: This test must be last! void parentingRepliesToTheApp(); private: @@ -600,7 +649,8 @@ public: int totalConnections; bool stopTransfer = false; - bool hasContent = false; + bool checkedContentLength = false; + bool foundContentLength = false; int contentRead = 0; int contentLength = 0; @@ -632,6 +682,7 @@ public: { contentLength = 0; receivedData.clear(); + foundContentLength = false; } protected: @@ -688,8 +739,13 @@ private: void parseContentLength() { - int index = receivedData.indexOf("Content-Length:"); - index += sizeof("Content-Length:") - 1; + int index = receivedData.indexOf("content-length:"); + if (index == -1) + return; + + foundContentLength = true; + + index += sizeof("content-length:") - 1; const auto end = std::find(receivedData.cbegin() + index, receivedData.cend(), '\r'); auto num = receivedData.mid(index, std::distance(receivedData.cbegin() + index, end)); bool ok; @@ -729,12 +785,14 @@ public slots: if (doubleEndlPos != -1) { const int endOfHeader = doubleEndlPos + 4; - hasContent = receivedData.startsWith("POST") || receivedData.startsWith("PUT") - || receivedData.startsWith("CUSTOM_WITH_PAYLOAD"); - if (hasContent && contentLength == 0) - parseContentLength(); contentRead = receivedData.size() - endOfHeader; - if (hasContent && contentRead < contentLength) + + if (!checkedContentLength) { + parseContentLength(); + checkedContentLength = true; + } + + if (contentRead < contentLength) return; // multiple requests incoming. remove the bytes of the current one @@ -835,7 +893,7 @@ public: qint64 cacheSize() const override { qint64 total = 0; - foreach (const CachedContent &entry, cache) + for (const auto &[_, entry] : cache.asKeyValueRange()) total += entry.second.size(); return total; } @@ -1460,11 +1518,11 @@ QString tst_QNetworkReply::runSimpleRequest(QNetworkAccessManager::Operation op, while (!reply->isFinished()) { QTimer::singleShot(20000, loop, SLOT(quit())); code = loop->exec(); - if (count == spy.count() && !reply->isFinished()) { + if (count == spy.size() && !reply->isFinished()) { code = Timeout; break; } - count = spy.count(); + count = spy.size(); } delete loop; loop = 0; @@ -1530,11 +1588,11 @@ 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.size() && !reply->isFinished()) { returnCode = Timeout; break; } - count = spy.count(); + count = spy.size(); } delete loop; loop = 0; @@ -1562,8 +1620,10 @@ void tst_QNetworkReply::initTestCase() testDataDir = QCoreApplication::applicationDirPath(); #if defined(QT_TEST_SERVER) - QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpServerName(), 21)); - QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpProxyServerName(), 2121)); + if (ftpSupported) { + QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpServerName(), 21)); + QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpProxyServerName(), 2121)); + } QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 80)); QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 443)); QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3128)); @@ -1976,6 +2036,253 @@ void tst_QNetworkReply::getFromHttp() QCOMPARE(reply->readAll(), reference.readAll()); } +void tst_QNetworkReply::getWithBodyFromHttp_data() +{ + QTest::addColumn<QByteArray>("dataFromClientToServer"); + QTest::addColumn<bool>("useDevice"); + QTest::newRow("with-bytearray") << QByteArray("Body 1") << false; + QTest::newRow("with-bytearray2") << QByteArray("Body 2") << false; + QTest::newRow("with-bytearray3") << QByteArray("Body 3") << false; + QTest::newRow("with-device") << QByteArray("Body 1") << true; + QTest::newRow("with-device2") << QByteArray("Body 2") << true; + QTest::newRow("with-device3") << QByteArray("Body 3") << true; +} + +void tst_QNetworkReply::getWithBodyFromHttp() +{ + QFETCH(QByteArray, dataFromClientToServer); + QFETCH(bool, useDevice); + + QBuffer buff; + buff.setData(dataFromClientToServer); + buff.open(QIODevice::ReadOnly); + + QByteArray dataFromServerToClient = QByteArray("Long first line\r\nLong second line"); + QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: "); + httpResponse += QByteArray::number(dataFromServerToClient.size()); + httpResponse += "\r\n\r\n"; + httpResponse += dataFromServerToClient; + + MiniHttpServer server(httpResponse); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + QNetworkReplyPtr reply; + + if (useDevice) + reply.reset(manager.get(request, &buff)); + else + reply.reset(manager.get(request, dataFromClientToServer)); + + QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); + QCOMPARE(server.contentLength, dataFromClientToServer.size()); + QCOMPARE(server.receivedData.right(dataFromClientToServer.size()), dataFromClientToServer); + QByteArray content = reply->readAll(); + QCOMPARE(content, dataFromServerToClient); +} + +void tst_QNetworkReply::getWithAndWithoutBodyFromHttp_data() +{ + QTest::addColumn<QByteArray>("dataFromClientToServer"); + QTest::addColumn<bool>("alwaysCache"); + QTest::addColumn<tst_QNetworkReply::RunSimpleRequestReturn>("requestReturn"); + QTest::addColumn<bool>("useDevice"); + QTest::newRow("with-bytearray") << QByteArray("Body 1") << false << Success << false; + QTest::newRow("with-bytearray2") << QByteArray("Body 2") << false << Success << false; + QTest::newRow("with-bytearray3") << QByteArray("Body 3") << false << Success << false; + QTest::newRow("with-bytearray-cache") << QByteArray("Body 1") << true << Failure << false; + QTest::newRow("with-bytearray-cache2") << QByteArray("Body 2") << true << Failure << false; + QTest::newRow("with-bytearray-cache3") << QByteArray("Body 3") << true << Failure << false; + QTest::newRow("with-device") << QByteArray("Body 1") << false << Success << true; + QTest::newRow("with-device2") << QByteArray("Body 2") << false << Success << true; + QTest::newRow("with-device3") << QByteArray("Body 3") << false << Success << true; + QTest::newRow("with-device-cache") << QByteArray("Body 1") << true << Failure << true; + QTest::newRow("with-device-cache2") << QByteArray("Body 2") << true << Failure << true; + QTest::newRow("with-device-cache3") << QByteArray("Body 3") << true << Failure << true; +} + +void tst_QNetworkReply::getWithAndWithoutBodyFromHttp() +{ + QFETCH(QByteArray, dataFromClientToServer); + QFETCH(bool, alwaysCache); + QFETCH(tst_QNetworkReply::RunSimpleRequestReturn, requestReturn); + QFETCH(bool, useDevice); + + QBuffer buff; + buff.setData(dataFromClientToServer); + buff.open(QIODevice::ReadOnly); + + QNetworkAccessManager qnam; + MyMemoryCache *memoryCache = new MyMemoryCache(&qnam); + qnam.setCache(memoryCache); + + const int sizeOfDataFromServerToClient =3; + QByteArray dataFromServerToClient1 = QByteArray("aaa"); + QByteArray dataFromServerToClient2 = QByteArray("bbb"); + QByteArray dataFromServerToClient3 = QByteArray("ccc"); + + QByteArray baseHttpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: "); + baseHttpResponse += QByteArray::number(sizeOfDataFromServerToClient); + baseHttpResponse += "\r\n\r\n"; + + MiniHttpServer server(baseHttpResponse + dataFromServerToClient1); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + + // Send request without body + QNetworkReplyPtr reply(manager.get(request)); + QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); + QByteArray content = reply->readAll(); + QCOMPARE(content, dataFromServerToClient1); + + if (alwaysCache) { + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, + QNetworkRequest::AlwaysCache); + } + + server.dataToTransmit = baseHttpResponse + dataFromServerToClient2; + + // Send request with body + QNetworkReplyPtr reply2; + if (useDevice) + reply2.reset(manager.get(request, &buff)); + else + reply2.reset(manager.get(request, dataFromClientToServer)); + + QVERIFY2(waitForFinish(reply2) == requestReturn, msgWaitForFinished(reply2)); + content = reply2->readAll(); + + if (alwaysCache) + QVERIFY(content.isEmpty()); + else + QCOMPARE(content, dataFromServerToClient2); + + QCOMPARE(reply2->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), false); + + if (alwaysCache) { + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, + QNetworkRequest::PreferNetwork); + } + + server.dataToTransmit = baseHttpResponse + dataFromServerToClient3; + + // Send another request without a body + QNetworkReplyPtr reply3(manager.get(request)); + QVERIFY2(waitForFinish(reply3) == Success, msgWaitForFinished(reply3)); + content = reply3->readAll(); + QCOMPARE(content, dataFromServerToClient3); + QCOMPARE(reply3->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), false); +} + +void tst_QNetworkReply::getWithBodyRedirected_data() +{ + QTest::addColumn<QByteArray>("dataFromClientToServer"); + QTest::addColumn<bool>("useDevice"); + QTest::addColumn<int>("status"); + QTest::newRow("with-bytearray - 301") << QByteArray("Body 1") << false << 301; + QTest::newRow("with-bytearray2 - 301") << QByteArray("Body 2") << false << 301; + QTest::newRow("with-bytearray3 - 301") << QByteArray("Body 3") << false << 301; + QTest::newRow("with-device - 301") << QByteArray("Body 1") << true << 301; + QTest::newRow("with-device2 - 301") << QByteArray("Body 2") << true << 301; + QTest::newRow("with-device3 - 301") << QByteArray("Body 3") << true << 301; + QTest::newRow("with-bytearray - 302") << QByteArray("Body 1") << false << 302; + QTest::newRow("with-bytearray2 - 302") << QByteArray("Body 2") << false << 302; + QTest::newRow("with-bytearray3 - 302") << QByteArray("Body 3") << false << 302; + QTest::newRow("with-device - 302") << QByteArray("Body 1") << true << 302; + QTest::newRow("with-device2 - 302") << QByteArray("Body 2") << true << 302; + QTest::newRow("with-device3 - 302") << QByteArray("Body 3") << true << 302; + QTest::newRow("with-bytearray - 307") << QByteArray("Body 1") << false << 307; + QTest::newRow("with-bytearray2 - 307") << QByteArray("Body 2") << false << 307; + QTest::newRow("with-bytearray3 - 307") << QByteArray("Body 3") << false << 307; + QTest::newRow("with-device - 307") << QByteArray("Body 1") << true << 307; + QTest::newRow("with-device2 - 307") << QByteArray("Body 2") << true << 307; + QTest::newRow("with-device3 - 307") << QByteArray("Body 3") << true << 307; + QTest::newRow("with-bytearray - 308") << QByteArray("Body 1") << false << 308; + QTest::newRow("with-bytearray2 - 308") << QByteArray("Body 2") << false << 308; + QTest::newRow("with-bytearray3 - 308") << QByteArray("Body 3") << false << 308; + QTest::newRow("with-device - 308") << QByteArray("Body 1") << true << 308; + QTest::newRow("with-device2 - 308") << QByteArray("Body 2") << true << 308; + QTest::newRow("with-device3 - 308") << QByteArray("Body 3") << true << 308; +} + +void tst_QNetworkReply::getWithBodyRedirected() +{ + QFETCH(QByteArray, dataFromClientToServer); + QFETCH(bool, useDevice); + QFETCH(int, status); + + QBuffer buff; + buff.setData(dataFromClientToServer); + buff.open(QIODevice::ReadOnly); + + QUrl localhost = QUrl("http://localhost"); + + // Setup server to which the second server will redirect to + MiniHttpServer server2(httpEmpty200Response); + + QUrl redirectUrl = QUrl(localhost); + redirectUrl.setPort(server2.serverPort()); + + QByteArray redirectReply; + switch (status) { + case 301: redirectReply = + foundReplyStr().arg(QString(redirectUrl.toEncoded())).toLatin1(); break; + case 302: redirectReply = + movedReplyStr().arg(QString(redirectUrl.toEncoded())).toLatin1(); break; + case 307: redirectReply = + tempRedirectReplyStr().arg(QString(redirectUrl.toEncoded())).toLatin1(); break; + case 308: redirectReply = + permRedirectReplyStr().arg(QString(redirectUrl.toEncoded())).toLatin1(); break; + default: QFAIL("Unexpected status code"); break; + } + + // Setup redirect server + MiniHttpServer server(redirectReply); + + localhost.setPort(server.serverPort()); + QNetworkRequest request(localhost); + request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, + QNetworkRequest::NoLessSafeRedirectPolicy); + + QNetworkReplyPtr reply; + if (useDevice) + reply.reset(manager.get(request, &buff)); + else + reply.reset(manager.get(request, dataFromClientToServer)); + + QSignalSpy redSpy(reply.data(), SIGNAL(redirected(QUrl))); + QSignalSpy finSpy(reply.data(), SIGNAL(finished())); + + QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); + + // Redirected and finished should be emitted exactly once + QCOMPARE(redSpy.size(), 1); + QCOMPARE(finSpy.size(), 1); + + // Original URL should not be changed after redirect + QCOMPARE(request.url(), localhost); + + // Verify Redirect url + QList<QVariant> args = redSpy.takeFirst(); + QCOMPARE(args.at(0).toUrl(), redirectUrl); + + // Reply url is set to the redirect url + QCOMPARE(reply->url(), redirectUrl); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(validateRedirectedResponseHeaders(reply)); + + // Verify that the message body has arrived to the server + if (status > 302) { + QVERIFY(server2.contentLength != 0); + QCOMPARE(server2.contentLength, dataFromClientToServer.size()); + QCOMPARE(server2.receivedData.right(dataFromClientToServer.size()), dataFromClientToServer); + } else { + // In these cases the message body should not reach the server + QVERIFY(server2.contentLength == 0); + } +} + #if QT_CONFIG(networkproxy) void tst_QNetworkReply::headFromHttp_data() { @@ -2277,9 +2584,9 @@ void tst_QNetworkReply::putToFtp() QSignalSpy spy(r, SIGNAL(downloadProgress(qint64,qint64))); while (!r->isFinished()) { QTestEventLoop::instance().enterLoop(10); - if (count == spy.count() && !r->isFinished()) + if (count == spy.size() && !r->isFinished()) break; - count = spy.count(); + count = spy.size(); } QObject::disconnect(r, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); @@ -2394,6 +2701,48 @@ void tst_QNetworkReply::putToHttpSynchronous() QCOMPARE(uploadedData, data); } +void tst_QNetworkReply::putWithoutBody_data() +{ + QTest::addColumn<bool>("client_data"); + + QTest::newRow("client_has_data") << true; + QTest::newRow("client_does_not_have_data") << false; +} + +void tst_QNetworkReply::putWithoutBody() +{ + QFETCH(bool, client_data); + + QBuffer buff; + + if (client_data) { + buff.setData("Dummy data from client to server"); + buff.open(QIODevice::ReadOnly); + } + + QByteArray dataFromServerToClient = QByteArray("Some ridiculous dummy data"); + QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: "); + httpResponse += QByteArray::number(dataFromServerToClient.size()); + httpResponse += "\r\n\r\n"; + httpResponse += dataFromServerToClient; + + MiniHttpServer server(httpResponse); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); + + QNetworkReplyPtr reply; + if (client_data) + reply.reset(manager.put(request, &buff)); + else + reply.reset(manager.put(request, nullptr)); + + QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); + QCOMPARE(server.foundContentLength, client_data); +} + + void tst_QNetworkReply::postToHttp_data() { putToFile_data(); @@ -2763,6 +3112,47 @@ void tst_QNetworkReply::multipartSkipIndices() // QTBUG-32534 multiPart->deleteLater(); } +void tst_QNetworkReply::postWithoutBody_data() +{ + QTest::addColumn<bool>("client_data"); + + QTest::newRow("client_has_data") << true; + QTest::newRow("client_does_not_have_data") << false; +} + +void tst_QNetworkReply::postWithoutBody() +{ + QFETCH(bool, client_data); + + QBuffer buff; + + if (client_data) { + buff.setData("Dummy data from client to server"); + buff.open(QIODevice::ReadOnly); + } + + QByteArray dataFromServerToClient = QByteArray("Some ridiculous dummy data"); + QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: "); + httpResponse += QByteArray::number(dataFromServerToClient.size()); + httpResponse += "\r\n\r\n"; + httpResponse += dataFromServerToClient; + + MiniHttpServer server(httpResponse); + server.doClose = true; + + QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); + request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); + + QNetworkReplyPtr reply; + if (client_data) + reply.reset(manager.post(request, &buff)); + else + reply.reset(manager.post(request, nullptr)); + + QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); + QCOMPARE(server.foundContentLength, client_data); +} + void tst_QNetworkReply::putToHttpMultipart_data() { postToHttpMultipart_data(); @@ -3154,7 +3544,7 @@ void tst_QNetworkReply::connectToIPv6Address() if (!QtNetworkSettings::hasIPv6()) QSKIP("system doesn't support ipv6!"); - QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: "); + QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\ncontent-length: "); httpResponse += QByteArray::number(dataToSend.size()); httpResponse += "\r\n\r\n"; httpResponse += dataToSend; @@ -3169,7 +3559,7 @@ void tst_QNetworkReply::connectToIPv6Address() QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); QByteArray content = reply->readAll(); //qDebug() << server.receivedData; - QByteArray hostinfo = "\r\nHost: " + hostfield + ':' + QByteArray::number(server.serverPort()) + "\r\n"; + QByteArray hostinfo = "\r\nhost: " + hostfield + ':' + QByteArray::number(server.serverPort()) + "\r\n"; QVERIFY(server.receivedData.contains(hostinfo)); QCOMPARE(content, dataToSend); QCOMPARE(reply->url(), request.url()); @@ -3360,7 +3750,9 @@ void tst_QNetworkReply::ioGetFromFtp() { QFETCH(QString, fileName); QFile reference(fileName); - reference.open(QIODevice::ReadOnly); // will fail for bigfile + const bool ok = reference.open(QIODevice::ReadOnly); // will fail for bigfile + if (fileName != "bigfile") + QVERIFY(ok); QNetworkRequest request("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/" + fileName); QNetworkReplyPtr reply(manager.get(request)); @@ -3385,7 +3777,7 @@ void tst_QNetworkReply::ioGetFromFtpWithReuse() QSKIP("FTP is not supported"); QString fileName = testDataDir + "/rfc3252.txt"; QFile reference(fileName); - reference.open(QIODevice::ReadOnly); + QVERIFY(reference.open(QIODevice::ReadOnly)); QNetworkRequest request(QUrl("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt")); @@ -3515,7 +3907,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth_data() QTest::addColumn<int>("expectedAuth"); QFile reference(testDataDir + "/rfc3252.txt"); - reference.open(QIODevice::ReadOnly); + QVERIFY(reference.open(QIODevice::ReadOnly)); QByteArray referenceData = reference.readAll(); QString httpServer = QtNetworkSettings::httpServerName(); QTest::newRow("basic") @@ -3589,7 +3981,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth() QCOMPARE(reader1.data, expectedData); QCOMPARE(reader2.data, expectedData); - QCOMPARE(authspy.count(), (expectedAuth ? 1 : 0)); + QCOMPARE(authspy.size(), (expectedAuth ? 1 : 0)); expectedAuth = qMax(0, expectedAuth - 1); } @@ -3610,7 +4002,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth() QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); QCOMPARE(reader.data, expectedData); - QCOMPARE(authspy.count(), (expectedAuth ? 1 : 0)); + QCOMPARE(authspy.size(), (expectedAuth ? 1 : 0)); expectedAuth = qMax(0, expectedAuth - 1); } @@ -3627,7 +4019,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth() // bad credentials in a synchronous request should just fail QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError); } else { - QCOMPARE(authspy.count(), 0); + QCOMPARE(authspy.size(), 0); // we cannot use a data reader here, since that connects to the readyRead signal, // just use readAll() @@ -3653,7 +4045,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth() // bad credentials in a synchronous request should just fail QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError); } else { - QCOMPARE(authspy.count(), 0); + QCOMPARE(authspy.size(), 0); // we cannot use a data reader here, since that connects to the readyRead signal, // just use readAll() @@ -3679,7 +4071,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuthSynchronous() QNetworkReplyPtr replySync(manager.get(request)); QVERIFY(replySync->isFinished()); // synchronous QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError); - QCOMPARE(authspy.count(), 0); + QCOMPARE(authspy.size(), 0); QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 401); } @@ -3720,7 +4112,7 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuth() QCOMPARE(reader1.data, referenceData); QCOMPARE(reader2.data, referenceData); - QCOMPARE(authspy.count(), 1); + QCOMPARE(authspy.size(), 1); } reference.seek(0); @@ -3743,7 +4135,7 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuth() QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); QCOMPARE(reader.data, reference.readAll()); - QCOMPARE(authspy.count(), 0); + QCOMPARE(authspy.size(), 0); } // now check with synchronous calls: @@ -3756,7 +4148,7 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuth() QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); QNetworkReplyPtr replySync(manager.get(request)); QVERIFY(replySync->isFinished()); // synchronous - QCOMPARE(authspy.count(), 0); + QCOMPARE(authspy.size(), 0); // we cannot use a data reader here, since that connects to the readyRead signal, // just use readAll() @@ -3784,7 +4176,7 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuthSynchronous() manager.setProxy(QNetworkProxy()); // reset QVERIFY(replySync->isFinished()); // synchronous QCOMPARE(replySync->error(), QNetworkReply::ProxyAuthenticationRequiredError); - QCOMPARE(authspy.count(), 0); + QCOMPARE(authspy.size(), 0); QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 407); } @@ -3816,7 +4208,7 @@ void tst_QNetworkReply::ioGetFromHttpWithSocksProxy() QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); QCOMPARE(reader.data, reference.readAll()); - QCOMPARE(authspy.count(), 0); + QCOMPARE(authspy.size(), 0); } // set an invalid proxy just to make sure that we can't load @@ -3840,10 +4232,9 @@ void tst_QNetworkReply::ioGetFromHttpWithSocksProxy() QVERIFY(reader.data.isEmpty()); QVERIFY(int(reply->error()) > 0); - QEXPECT_FAIL("", "QTcpSocket doesn't return enough information yet", Continue); QCOMPARE(int(reply->error()), int(QNetworkReply::ProxyConnectionRefusedError)); - QCOMPARE(authspy.count(), 0); + QCOMPARE(authspy.size(), 0); } } #endif // QT_CONFIG(networkproxy) @@ -3871,7 +4262,7 @@ void tst_QNetworkReply::ioGetFromHttpsWithSslErrors() QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); QCOMPARE(reader.data, reference.readAll()); - QCOMPARE(sslspy.count(), 1); + QCOMPARE(sslspy.size(), 1); QVERIFY(!storedSslConfiguration.isNull()); QVERIFY(!reply->sslConfiguration().isNull()); @@ -3899,7 +4290,7 @@ void tst_QNetworkReply::ioGetFromHttpsWithIgnoreSslErrors() QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); QCOMPARE(reader.data, reference.readAll()); - QCOMPARE(sslspy.count(), 1); + QCOMPARE(sslspy.size(), 1); QVERIFY(!storedSslConfiguration.isNull()); QVERIFY(!reply->sslConfiguration().isNull()); @@ -3922,7 +4313,7 @@ void tst_QNetworkReply::ioGetFromHttpsWithSslHandshakeError() QCOMPARE(waitForFinish(reply), int(Failure)); QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError); - QCOMPARE(sslspy.count(), 0); + QCOMPARE(sslspy.size(), 0); } #endif @@ -3980,11 +4371,11 @@ void tst_QNetworkReply::ioGetFromHttpBrokenServer() QCOMPARE(waitForFinish(reply), int(Failure)); QCOMPARE(reply->url(), request.url()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(reply->error() != QNetworkReply::NoError); } -void tst_QNetworkReply::ioGetFromHttpStatus100_data() +void tst_QNetworkReply::ioGetFromHttpStatusInformational_data() { QTest::addColumn<QByteArray>("dataToSend"); QTest::addColumn<int>("statusCode"); @@ -3995,9 +4386,25 @@ void tst_QNetworkReply::ioGetFromHttpStatus100_data() QTest::newRow("minimal+404") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 204 No Content\r\n\r\n") << 204; QTest::newRow("with_headers") << QByteArray("HTTP/1.1 100 Continue\r\nBla: x\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; QTest::newRow("with_headers2") << QByteArray("HTTP/1.1 100 Continue\nBla: x\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + + QTest::newRow("normal-custom") << QByteArray("HTTP/1.1 133 Custom\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + QTest::newRow("minimal-custom") << QByteArray("HTTP/1.1 133 Custom\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + QTest::newRow("minimal2-custom") << QByteArray("HTTP/1.1 133 Custom\n\nHTTP/1.0 200 OK\r\n\r\n") << 200; + QTest::newRow("minimal3-custom") << QByteArray("HTTP/1.1 133 Custom\n\nHTTP/1.0 200 OK\n\n") << 200; + QTest::newRow("minimal+404-custom") << QByteArray("HTTP/1.1 133 Custom\n\nHTTP/1.0 204 No Content\r\n\r\n") << 204; + QTest::newRow("with_headers-custom") << QByteArray("HTTP/1.1 133 Custom\r\nBla: x\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + QTest::newRow("with_headers2-custom") << QByteArray("HTTP/1.1 133 Custom\nBla: x\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + + QTest::newRow("normal-custom2") << QByteArray("HTTP/1.1 179 Custom2\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + QTest::newRow("minimal-custom2") << QByteArray("HTTP/1.1 179 Custom2\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + QTest::newRow("minimal2-custom2") << QByteArray("HTTP/1.1 179 Custom2\n\nHTTP/1.0 200 OK\r\n\r\n") << 200; + QTest::newRow("minimal3-custom2") << QByteArray("HTTP/1.1 179 Custom2\n\nHTTP/1.0 200 OK\n\n") << 200; + QTest::newRow("minimal+404-custom2") << QByteArray("HTTP/1.1 179 Custom2\n\nHTTP/1.0 204 No Content\r\n\r\n") << 204; + QTest::newRow("with_headers-custom2") << QByteArray("HTTP/1.1 179 Custom2\r\nBla: x\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; + QTest::newRow("with_headers2-custom2") << QByteArray("HTTP/1.1 179 Custom2\nBla: x\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200; } -void tst_QNetworkReply::ioGetFromHttpStatus100() +void tst_QNetworkReply::ioGetFromHttpStatusInformational() { QFETCH(QByteArray, dataToSend); QFETCH(int, statusCode); @@ -4316,15 +4723,15 @@ void tst_QNetworkReply::ioGetWithManyProxies_data() // Tests that fail: - // HTTP request with FTP caching proxy - proxyList.clear(); - proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121); - QTest::newRow("http-on-ftp") - << proxyList << QNetworkProxy() - << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" - << QNetworkReply::ProxyNotFoundError; - if (ftpSupported) { + // HTTP request with FTP caching proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121); + QTest::newRow("http-on-ftp") + << proxyList << QNetworkProxy() + << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" + << QNetworkReply::ProxyNotFoundError; + // FTP request with HTTP caching proxy proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, @@ -4355,13 +4762,15 @@ void tst_QNetworkReply::ioGetWithManyProxies_data() << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" << QNetworkReply::ProxyNotFoundError; - // HTTPS with FTP caching proxy - proxyList.clear(); - proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121); - QTest::newRow("https-on-ftp") - << proxyList << QNetworkProxy() - << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" - << QNetworkReply::ProxyNotFoundError; + if (ftpSupported) { + // HTTPS with FTP caching proxy + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121); + QTest::newRow("https-on-ftp") + << proxyList << QNetworkProxy() + << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" + << QNetworkReply::ProxyNotFoundError; + } #endif // Complex requests: @@ -4384,15 +4793,17 @@ void tst_QNetworkReply::ioGetWithManyProxies_data() << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" << QNetworkReply::NoError; - // HTTP request with FTP + HTTP + SOCKS - proxyList.clear(); - proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121) - << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129) - << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081); - QTest::newRow("http-on-ftp+http+socks") - << proxyList << proxyList.at(1) // second proxy should be used - << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" - << QNetworkReply::NoError; + if (ftpSupported) { + // HTTP request with FTP + HTTP + SOCKS + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081); + QTest::newRow("http-on-ftp+http+socks") + << proxyList << proxyList.at(1) // second proxy should be used + << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + } // HTTP request with NoProxy + HTTP proxyList.clear(); @@ -4404,15 +4815,15 @@ void tst_QNetworkReply::ioGetWithManyProxies_data() << QNetworkReply::NoError; // HTTP request with FTP + NoProxy - proxyList.clear(); - proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121) - << QNetworkProxy(QNetworkProxy::NoProxy); - QTest::newRow("http-on-ftp+noproxy") - << proxyList << proxyList.at(1) // second proxy should be used - << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" - << QNetworkReply::NoError; - if (ftpSupported) { + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121) + << QNetworkProxy(QNetworkProxy::NoProxy); + QTest::newRow("http-on-ftp+noproxy") + << proxyList << proxyList.at(1) // second proxy should be used + << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + // FTP request with HTTP Caching + FTP proxyList.clear(); proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, @@ -4435,15 +4846,17 @@ void tst_QNetworkReply::ioGetWithManyProxies_data() << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" << QNetworkReply::NoError; - // HTTPS request with FTP + HTTP C + HTTP T - proxyList.clear(); - proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121) - << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129) - << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3129); - QTest::newRow("https-on-ftp+httpcaching+http") - << proxyList << proxyList.at(2) // skip the first two - << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" - << QNetworkReply::NoError; + if (ftpSupported) { + // HTTPS request with FTP + HTTP C + HTTP T + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129) + << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3129); + QTest::newRow("https-on-ftp+httpcaching+http") + << proxyList << proxyList.at(2) // skip the first two + << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt" + << QNetworkReply::NoError; + } #endif } @@ -4498,16 +4911,16 @@ void tst_QNetworkReply::ioGetWithManyProxies() // now verify that the proxies worked: QFETCH(QNetworkProxy, proxyUsed); if (proxyUsed.type() == QNetworkProxy::NoProxy) { - QCOMPARE(authspy.count(), 0); + QCOMPARE(authspy.size(), 0); } else { if (QByteArray(QTest::currentDataTag()).startsWith("ftp-")) return; // No authentication with current FTP or with FTP proxies - QCOMPARE(authspy.count(), 1); + QCOMPARE(authspy.size(), 1); QCOMPARE(qvariant_cast<QNetworkProxy>(authspy.at(0).at(0)), proxyUsed); } } else { // request failed - QCOMPARE(authspy.count(), 0); + QCOMPARE(authspy.size(), 0); } } #endif // QT_CONFIG(networkproxy) @@ -4675,6 +5088,10 @@ void tst_QNetworkReply::ioPutToFileFromProcess() QByteArray contents = file.readAll(); QCOMPARE(contents, data); + if (process.state() == QProcess::Running) + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitCode(), 0); + #endif // QT_CONFIG(process) } @@ -4891,8 +5308,8 @@ void tst_QNetworkReply::ioPostToHttpFromSocket() QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex()); - QTEST(int(authenticationRequiredSpy.count()), "authenticationRequiredCount"); - QTEST(int(proxyAuthenticationRequiredSpy.count()), "proxyAuthenticationRequiredCount"); + QTEST(int(authenticationRequiredSpy.size()), "authenticationRequiredCount"); + QTEST(int(proxyAuthenticationRequiredSpy.size()), "proxyAuthenticationRequiredCount"); } void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous_data() @@ -5364,13 +5781,9 @@ void tst_QNetworkReply::emitAllUploadProgressSignals() QNetworkRequest catchAllSignalsRequest(normalRequest); catchAllSignalsRequest.setAttribute(QNetworkRequest::EmitAllUploadProgressSignalsAttribute, true); - QList<QNetworkRequest> requests; - requests << normalRequest << catchAllSignalsRequest; - QList<int> signalCount; - foreach (const QNetworkRequest &request, requests) { - + for (const QNetworkRequest &request : {normalRequest, catchAllSignalsRequest}) { sourceFile.seek(0); QNetworkReplyPtr reply(manager.post(request, &sourceFile)); QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64))); @@ -5391,7 +5804,7 @@ void tst_QNetworkReply::emitAllUploadProgressSignals() QVERIFY(!QTestEventLoop::instance().timeout()); incomingSocket->close(); - signalCount.append(spy.count()); + signalCount.append(spy.size()); reply->deleteLater(); } server.close(); @@ -5437,7 +5850,7 @@ void tst_QNetworkReply::ioPostToHttpEmptyUploadProgress() QVERIFY(!QTestEventLoop::instance().timeout()); // final check: only 1 uploadProgress has been emitted - QCOMPARE(spy.length(), 1); + QCOMPARE(spy.size(), 1); QList<QVariant> args = spy.last(); QVERIFY(!args.isEmpty()); QCOMPARE(args.at(0).toLongLong(), buffer.size()); @@ -5475,7 +5888,7 @@ void tst_QNetworkReply::lastModifiedHeaderForHttp() QDateTime header = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime(); QDateTime realDate = QDateTime::fromString("2007-05-22T12:04:57", Qt::ISODate); - realDate.setTimeSpec(Qt::UTC); + realDate.setTimeZone(QTimeZone::UTC); QCOMPARE(header, realDate); } @@ -5608,7 +6021,7 @@ void tst_QNetworkReply::downloadProgress() QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY(reply->isFinished()); - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); //final progress should have equal current & total QList<QVariant> args = spy.takeLast(); @@ -5654,14 +6067,14 @@ void tst_QNetworkReply::uploadProgress() QVERIFY(server.hasPendingConnections()); QTcpSocket *receiver = server.nextPendingConnection(); - if (finished.count() == 0) { + if (finished.size() == 0) { // it's not finished yet, so wait for it to be QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); } delete receiver; - QVERIFY(finished.count() > 0); - QVERIFY(spy.count() > 0); + QVERIFY(finished.size() > 0); + QVERIFY(spy.size() > 0); QList<QVariant> args = spy.last(); QCOMPARE(args.at(0).toInt(), data.size()); @@ -5943,8 +6356,8 @@ void tst_QNetworkReply::nestedEventLoops() QTestEventLoop::instance().enterLoop(20); QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout"); - QCOMPARE(finishedspy.count(), 1); - QCOMPARE(errorspy.count(), 0); + QCOMPARE(finishedspy.size(), 1); + QCOMPARE(errorspy.size(), 0); } #if QT_CONFIG(networkproxy) @@ -5977,7 +6390,7 @@ void tst_QNetworkReply::httpProxyCommands() manager.setProxy(proxy); QNetworkRequest request(url); - request.setRawHeader("User-Agent", "QNetworkReplyAutoTest/1.0"); + request.setRawHeader("user-agent", "QNetworkReplyAutoTest/1.0"); QNetworkReplyPtr reply(manager.get(request)); // wait for the finished signal @@ -5994,10 +6407,11 @@ void tst_QNetworkReply::httpProxyCommands() QCOMPARE(receivedHeader, expectedCommand); //QTBUG-17223 - make sure the user agent from the request is sent to proxy server even for CONNECT - int uapos = proxyServer.receivedData.indexOf("User-Agent"); + const QByteArray cUserAgent = "user-agent: "; + int uapos = proxyServer.receivedData.toLower().indexOf(cUserAgent) + cUserAgent.size(); int uaend = proxyServer.receivedData.indexOf("\r\n", uapos); QByteArray uaheader = proxyServer.receivedData.mid(uapos, uaend - uapos); - QCOMPARE(uaheader, QByteArray("User-Agent: QNetworkReplyAutoTest/1.0")); + QCOMPARE(uaheader, QByteArray("QNetworkReplyAutoTest/1.0")); } class ProxyChangeHelper : public QObject @@ -6034,14 +6448,6 @@ struct QThreadCleanup } }; -struct QDeleteLaterCleanup -{ - static inline void cleanup(QObject *o) - { - o->deleteLater(); - } -}; - #if QT_CONFIG(networkproxy) void tst_QNetworkReply::httpProxyCommandsSynchronous() { @@ -6053,7 +6459,7 @@ void tst_QNetworkReply::httpProxyCommandsSynchronous() // the server thread, because the client is never returning to the // event loop QScopedPointer<QThread, QThreadCleanup> serverThread(new QThread); - QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> proxyServer(new MiniHttpServer(responseToSend, false, serverThread.data())); + QScopedPointer<MiniHttpServer, QScopedPointerDeleteLater> proxyServer(new MiniHttpServer(responseToSend, false, serverThread.data())); QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer->serverPort()); manager.setProxy(proxy); @@ -6171,9 +6577,9 @@ void tst_QNetworkReply::authorizationError() QCOMPARE(waitForFinish(reply), int(Failure)); QFETCH(int, errorSignalCount); - QCOMPARE(errorSpy.count(), errorSignalCount); + QCOMPARE(errorSpy.size(), errorSignalCount); QFETCH(int, finishedSignalCount); - QCOMPARE(finishedSpy.count(), finishedSignalCount); + QCOMPARE(finishedSpy.size(), finishedSignalCount); QFETCH(int, error); QCOMPARE(reply->error(), QNetworkReply::NetworkError(error)); @@ -6211,7 +6617,6 @@ void tst_QNetworkReply::httpConnectionCount() } QVERIFY(server->listen()); - QCoreApplication::instance()->processEvents(); QUrl url("http://127.0.0.1:" + QString::number(server->serverPort()) + QLatin1Char('/')); if (encrypted) @@ -6222,7 +6627,7 @@ void tst_QNetworkReply::httpConnectionCount() QUrl urlCopy = url; urlCopy.setPath(u'/' + QString::number(i)); // Differentiate the requests a bit QNetworkRequest request(urlCopy); - request.setAttribute(QNetworkRequest::Http2AllowedAttribute, http2Enabled); + request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, http2Enabled); QNetworkReply* reply = manager.get(request); reply->setParent(server.data()); if (encrypted) @@ -6230,26 +6635,40 @@ void tst_QNetworkReply::httpConnectionCount() } int pendingConnectionCount = 0; - QElapsedTimer timer; - timer.start(); - while(pendingConnectionCount <= 20) { - QTestEventLoop::instance().enterLoop(1); + using namespace std::chrono_literals; + const auto newPendingConnection = [&server]() { return server->hasPendingConnections(); }; + // If we have http2 enabled then the second connection will take a little + // longer to be established because we will wait for the first one to finish + // to see if we should upgrade: + const int rampDown = http2Enabled ? 2 : 1; + while (pendingConnectionCount <= 6) { + if (!QTest::qWaitFor(newPendingConnection, pendingConnectionCount >= rampDown ? 3s : 7s)) + break; QTcpSocket *socket = server->nextPendingConnection(); - while (socket != 0) { - if (pendingConnectionCount == 0) { - // respond to the first connection so we know to transition to HTTP/1.1 when using - // HTTP/2 - socket->write(httpEmpty200Response); + while (socket) { + if (pendingConnectionCount == 0 && http2Enabled) { + // Respond to the first connection so we know to transition to HTTP/1.1 when using + // HTTP/2. + // Because of some internal state machinery we need to wait until the request has + // actually been written to the server before we can reply. + auto connection = std::make_shared<QMetaObject::Connection>(); + auto replyOnRequest = [=, buffer = QByteArray()]() mutable { + buffer += socket->readAll(); + if (!buffer.contains("\r\n\r\n")) + return; + socket->write(httpEmpty200Response); + QObject::disconnect(*connection); + }; + *connection = QObject::connect(socket, &QTcpSocket::readyRead, socket, + std::move(replyOnRequest)); + if (socket->bytesAvailable()) // If we already have data, check it now + emit socket->readyRead(); } pendingConnectionCount++; socket->setParent(server.data()); socket = server->nextPendingConnection(); } - - // at max. wait 10 sec - if (timer.elapsed() > 10000) - break; } QCOMPARE(pendingConnectionCount, 6); @@ -6532,7 +6951,7 @@ void tst_QNetworkReply::encrypted() QTestEventLoop::instance().enterLoop(20); QVERIFY(!QTestEventLoop::instance().timeout()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); reply->deleteLater(); } @@ -6561,7 +6980,7 @@ void tst_QNetworkReply::abortOnEncrypted() }); QSignalSpy spyEncrypted(reply, &QNetworkReply::encrypted); - QTRY_COMPARE(spyEncrypted.count(), 1); + QTRY_COMPARE(spyEncrypted.size(), 1); // Wait for the socket to be closed again in order to be sure QTcpSocket::readyRead would have been emitted. QTRY_VERIFY(server.socket != nullptr); @@ -6755,7 +7174,20 @@ void tst_QNetworkReply::getAndThenDeleteObject() // see https://bugs.webkit.org/show_bug.cgi?id=38935 void tst_QNetworkReply::symbianOpenCDataUrlCrash() { - QString requestUrl("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAWCAYAAAA1vze2AAAAB3RJTUUH2AUSEgolrgBvVQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAARnQU1BAACxjwv8YQUAAAHlSURBVHja5VbNShxBEK6ZaXtnHTebQPA1gngNmfaeq+QNPIlIXkC9iQdJxJNvEHLN3VkxhxxE8gTmEhAVddXZ6Z3f9Ndriz89/sHmkBQUVVT1fB9d9c3uOERUKTunIdn3HzstxGpYBDS4wZk7TAJj/wlJ90J+jnuygqs8svSj+/rGHBos3rE18XBvfU3no7NzlJfUaY/5whAwl8Lr/WDUv4ODxTMb+P5xLExe5LmO559WqTX/MQR4WZYEAtSePS4pE0qSnuhnRUcBU5Gm2k9XljU4Z26I3NRxBrd80rj2fh+KNE0FY4xevRgTjREvPFpasAK8Xli6MUbbuKw3afAGgSBXozo5u4hkmncAlkl5wx8iMGbdyQjnCFEiEwGiosj1UQA/x2rVddiVoi+l4IxE0PTDnx+mrQBvvnx9cFz3krhVvuhzFn579/aq/n5rW8fbtTqiWhIQZEo17YBvbkxOXNVndnYpTvod7AtiuN2re0+siwcB9oH8VxxrNwQQAhzyRs30n7wTI2HIN2g2QtQwjjhJIQatOq7E8bIVCLwzpl83Lvtvl+NohWWlE8UZTWEMAGCcR77fHKhPnZF5tYie6dfdxCphACmLPM+j8bYfmTryg64kV9Vh3mV8jP0b/4wO/YUPiT/8i0MLf55lSQAAAABJRU5ErkJggg=="); + QString requestUrl("data:image/" + "png;base64," + "iVBORw0KGgoAAAANSUhEUgAAABkAAAAWCAYAAAA1vze2AAAAB3RJTUUH2AUSEgolrgBvVQAAAAl" + "wSFlzAAALEwAACxMBAJqcGAAAAARnQU1BAACxjwv8YQUAAAHlSURBVHja5VbNShxBEK6ZaXtnHT" + "ebQPA1gngNmfaeq+QNPIlIXkC9iQdJxJNvEHLN3VkxhxxE8gTmEhAVddXZ6Z3f9Ndriz89/" + "sHmkBQUVVT1fB9d9c3uOERUKTunIdn3HzstxGpYBDS4wZk7TAJj/wlJ90J+jnuygqs8svSj+/" + "rGHBos3rE18XBvfU3no7NzlJfUaY/5whAwl8Lr/WDUv4ODxTMb+P5xLExe5LmO559WqTX/" + "MQR4WZYEAtSePS4pE0qSnuhnRUcBU5Gm2k9XljU4Z26I3NRxBrd80rj2fh+" + "KNE0FY4xevRgTjREvPFpasAK8Xli6MUbbuKw3afAGgSBXozo5u4hkmncAlkl5wx8iMGbdyQjnCF" + "EiEwGiosj1UQA/x2rVddiVoi+l4IxE0PTDnx+mrQBvvnx9cFz3krhVvuhzFn579/aq/" + "n5rW8fbtTqiWhIQZEo17YBvbkxOXNVndnYpTvod7AtiuN2re0+" + "siwcB9oH8VxxrNwQQAhzyRs30n7wTI2HIN2g2QtQwjjhJIQatOq7E8bIVCLwzpl83Lvtvl+" + "NohWWlE8UZTWEMAGCcR77fHKhPnZF5tYie6dfdxCphACmLPM+j8bYfmTryg64kV9Vh3mV8jP0b/" + "4wO/YUPiT/8i0MLf55lSQAAAABJRU5ErkJggg=="); QUrl url = QUrl::fromEncoded(requestUrl.toLatin1()); QNetworkRequest req(url); QNetworkReplyPtr reply; @@ -7144,9 +7576,9 @@ void tst_QNetworkReply::qtbug4121unknownAuthentication() QTestEventLoop::instance().enterLoop(10); QVERIFY(!QTestEventLoop::instance().timeout()); - QCOMPARE(authSpy.count(), 0); - QCOMPARE(finishedSpy.count(), 1); - QCOMPARE(errorSpy.count(), 1); + QCOMPARE(authSpy.size(), 0); + QCOMPARE(finishedSpy.size(), 1); + QCOMPARE(errorSpy.size(), 1); QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError); } @@ -7240,8 +7672,8 @@ void tst_QNetworkReply::authenticationCacheAfterCancel() QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(reply->error(), QNetworkReply::ProxyAuthenticationRequiredError); - QCOMPARE(authSpy.count(), 0); - QCOMPARE(proxyAuthSpy.count(), 1); + QCOMPARE(authSpy.size(), 0); + QCOMPARE(proxyAuthSpy.size(), 1); proxyAuthSpy.clear(); //should fail due to bad credentials @@ -7255,8 +7687,8 @@ void tst_QNetworkReply::authenticationCacheAfterCancel() // Work round known quirk in the old test server (danted -v < v1.1.19): if (reply->error() != QNetworkReply::HostNotFoundError) QCOMPARE(reply->error(), QNetworkReply::ProxyAuthenticationRequiredError); - QCOMPARE(authSpy.count(), 0); - QVERIFY(proxyAuthSpy.count() > 0); + QCOMPARE(authSpy.size(), 0); + QVERIFY(proxyAuthSpy.size() > 0); proxyAuthSpy.clear(); // QTBUG-23136 workaround (needed even with danted v1.1.19): @@ -7281,10 +7713,10 @@ void tst_QNetworkReply::authenticationCacheAfterCancel() QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError); - QVERIFY(authSpy.count() > 0); + QVERIFY(authSpy.size() > 0); authSpy.clear(); if (proxyAuth) { - QVERIFY(proxyAuthSpy.count() > 0); + QVERIFY(proxyAuthSpy.size() > 0); proxyAuthSpy.clear(); } @@ -7297,11 +7729,11 @@ void tst_QNetworkReply::authenticationCacheAfterCancel() QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError); - QVERIFY(authSpy.count() > 0); + QVERIFY(authSpy.size() > 0); authSpy.clear(); if (proxyAuth) { //should be supplied from cache - QCOMPARE(proxyAuthSpy.count(), 0); + QCOMPARE(proxyAuthSpy.size(), 0); proxyAuthSpy.clear(); } @@ -7315,11 +7747,11 @@ void tst_QNetworkReply::authenticationCacheAfterCancel() QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(reply->error(), QNetworkReply::NoError); - QVERIFY(authSpy.count() > 0); + QVERIFY(authSpy.size() > 0); authSpy.clear(); if (proxyAuth) { //should be supplied from cache - QCOMPARE(proxyAuthSpy.count(), 0); + QCOMPARE(proxyAuthSpy.size(), 0); proxyAuthSpy.clear(); } @@ -7331,11 +7763,11 @@ void tst_QNetworkReply::authenticationCacheAfterCancel() QCOMPARE(reply->error(), QNetworkReply::NoError); //should be supplied from cache - QCOMPARE(authSpy.count(), 0); + QCOMPARE(authSpy.size(), 0); authSpy.clear(); if (proxyAuth) { //should be supplied from cache - QCOMPARE(proxyAuthSpy.count(), 0); + QCOMPARE(proxyAuthSpy.size(), 0); proxyAuthSpy.clear(); } @@ -7437,8 +7869,8 @@ void tst_QNetworkReply::httpWithNoCredentialUsage() QNetworkReplyPtr reply(manager.get(request)); QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); // credentials in URL, so don't expect authentication signal - QCOMPARE(authSpy.count(), 0); - QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(authSpy.size(), 0); + QCOMPARE(finishedSpy.size(), 1); finishedSpy.clear(); } @@ -7448,8 +7880,8 @@ void tst_QNetworkReply::httpWithNoCredentialUsage() QNetworkReplyPtr reply(manager.get(request)); QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); // credentials in cache, so don't expect authentication signal - QCOMPARE(authSpy.count(), 0); - QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(authSpy.size(), 0); + QCOMPARE(finishedSpy.size(), 1); finishedSpy.clear(); } @@ -7466,9 +7898,9 @@ void tst_QNetworkReply::httpWithNoCredentialUsage() QVERIFY(!QTestEventLoop::instance().timeout()); // We check if authenticationRequired was emitted, however we do not anything in it so it should be 401 - QCOMPARE(authSpy.count(), 1); - QCOMPARE(finishedSpy.count(), 1); - QCOMPARE(errorSpy.count(), 1); + QCOMPARE(authSpy.size(), 1); + QCOMPARE(finishedSpy.size(), 1); + QCOMPARE(errorSpy.size(), 1); QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError); } @@ -7753,8 +8185,8 @@ void tst_QNetworkReply::qtbug45581WrongReplyStatusCode() QCOMPARE(reply->readAll(), expectedContent); - QCOMPARE(finishedSpy.count(), 0); - QCOMPARE(sslErrorsSpy.count(), 0); + QCOMPARE(finishedSpy.size(), 0); + QCOMPARE(sslErrorsSpy.size(), 0); QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), expectedContent.size()); @@ -7843,8 +8275,8 @@ void tst_QNetworkReply::synchronousRequest() QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>))); RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0)); QVERIFY(reply->isFinished()); - QCOMPARE(finishedSpy.count(), 0); - QCOMPARE(sslErrorsSpy.count(), 0); + QCOMPARE(finishedSpy.size(), 0); + QCOMPARE(sslErrorsSpy.size(), 0); QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType); @@ -7853,7 +8285,7 @@ void tst_QNetworkReply::synchronousRequest() if (expected.startsWith("file:")) { QString path = expected.mid(5); QFile file(path); - file.open(QIODevice::ReadOnly); + QVERIFY(file.open(QIODevice::ReadOnly)); expectedContent = file.readAll(); } else if (expected.startsWith("data:")) { expectedContent = expected.mid(5).toUtf8(); @@ -7883,7 +8315,7 @@ void tst_QNetworkReply::synchronousRequestSslFailure() runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0); QVERIFY(reply->isFinished()); QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError); - QCOMPARE(sslErrorsSpy.count(), 0); + QCOMPARE(sslErrorsSpy.size(), 0); } #endif @@ -8102,6 +8534,61 @@ void tst_QNetworkReply::varyingCacheExpiry() QVERIFY(success); } +class Qtbug25280Server : public MiniHttpServer +{ +public: + Qtbug25280Server(QByteArray qba) : MiniHttpServer(qba, false) {} + QSet<QTcpSocket*> receivedSockets; + void reply() override + { + // Save sockets in a list + receivedSockets.insert((QTcpSocket*)sender()); + qobject_cast<QTcpSocket*>(sender())->write(dataToTransmit); + //qDebug() << "count=" << receivedSockets.count(); + } +}; + +void tst_QNetworkReply::amountOfHttp1ConnectionsQtbug25280_data() +{ + QTest::addColumn<int>("amount"); + QTest::addRow("default") << 6; + QTest::addRow("minimize") << 1; + QTest::addRow("increase") << 12; +} + +// Also kind of QTBUG-8468 +void tst_QNetworkReply::amountOfHttp1ConnectionsQtbug25280() +{ + QFETCH(const int, amount); + QNetworkAccessManager manager; // function local instance + Qtbug25280Server server(tst_QNetworkReply::httpEmpty200Response); + server.doClose = false; + server.multiple = true; + QUrl url(QLatin1String("http://127.0.0.1")); // not "localhost" to prevent "Happy Eyeballs" + // from skewing the counting + url.setPort(server.serverPort()); + std::optional<QHttp1Configuration> http1Configuration; + if (amount != 6) // don't set if it's the default + http1Configuration.emplace().setNumberOfConnectionsPerHost(amount); + constexpr int NumRequests = 200; // send a lot more than we have sockets + int finished = 0; + std::array<std::unique_ptr<QNetworkReply>, NumRequests> replies; + for (auto &reply : replies) { + QNetworkRequest request(url); + if (http1Configuration) + request.setHttp1Configuration(*http1Configuration); + reply.reset(manager.get(request)); + QObject::connect(reply.get(), &QNetworkReply::finished, + [&finished] { ++finished; }); + } + QTRY_COMPARE_WITH_TIMEOUT(finished, NumRequests, 60'000); + for (const auto &reply : replies) { + QCOMPARE(reply->error(), QNetworkReply::NoError); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + } + QCOMPARE(server.receivedSockets.size(), amount); +} + void tst_QNetworkReply::dontInsertPartialContentIntoTheCache() { QByteArray reply206 = @@ -8147,7 +8634,7 @@ void tst_QNetworkReply::httpUserAgent() QVERIFY(reply->isFinished()); QCOMPARE(reply->error(), QNetworkReply::NoError); - QVERIFY(server.receivedData.contains("\r\nUser-Agent: abcDEFghi\r\n")); + QVERIFY(server.receivedData.contains("\r\nuser-agent: abcDEFghi\r\n")); } void tst_QNetworkReply::synchronousAuthenticationCache() @@ -8167,7 +8654,7 @@ void tst_QNetworkReply::synchronousAuthenticationCache() "Content-Type: text/plain\r\n" "\r\n" "auth"; - QRegularExpression rx("Authorization: Basic ([^\r\n]*)\r\n"); + QRegularExpression rx("authorization: Basic ([^\r\n]*)\r\n"); QRegularExpressionMatch match = rx.match(receivedData); if (match.hasMatch()) { if (QByteArray::fromBase64(match.captured(1).toLatin1()) == "login:password") { @@ -8188,7 +8675,7 @@ void tst_QNetworkReply::synchronousAuthenticationCache() // the server thread, because the client is never returning to the // event loop QScopedPointer<QThread, QThreadCleanup> serverThread(new QThread); - QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> server(new MiniAuthServer(serverThread.data())); + QScopedPointer<MiniHttpServer, QScopedPointerDeleteLater> server(new MiniAuthServer(serverThread.data())); server->doClose = true; //1) URL without credentials, we are not authenticated @@ -8327,31 +8814,33 @@ void tst_QNetworkReply::ftpAuthentication() void tst_QNetworkReply::emitErrorForAllReplies() // QTBUG-36890 { // port 100 is not well-known and should be closed - QList<QUrl> urls = QList<QUrl>() << QUrl("http://localhost:100/request1") - << QUrl("http://localhost:100/request2") - << QUrl("http://localhost:100/request3"); - QList<QNetworkReply *> replies; - QList<QSignalSpy *> errorSpies; - QList<QSignalSpy *> finishedSpies; - for (int a = 0; a < urls.size(); ++a) { - QNetworkRequest request(urls.at(a)); + const QUrl urls[] = { + QUrl("http://localhost:100/request1"), + QUrl("http://localhost:100/request2"), + QUrl("http://localhost:100/request3"), + }; + constexpr auto NUrls = std::size(urls); + + std::unique_ptr<QNetworkReply, QScopedPointerDeleteLater> replies[NUrls]; + std::optional<QSignalSpy> errorSpies[NUrls]; + std::optional<QSignalSpy> finishedSpies[NUrls]; + + for (size_t i = 0; i < NUrls; ++i) { + QNetworkRequest request(urls[i]); QNetworkReply *reply = manager.get(request); - replies.append(reply); - QSignalSpy *errorSpy = new QSignalSpy(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError))); - errorSpies.append(errorSpy); - QSignalSpy *finishedSpy = new QSignalSpy(reply, SIGNAL(finished())); - finishedSpies.append(finishedSpy); + replies[i].reset(reply); + errorSpies[i].emplace(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError))); + finishedSpies[i].emplace(reply, SIGNAL(finished())); QObject::connect(reply, SIGNAL(finished()), SLOT(emitErrorForAllRepliesSlot())); } + QTestEventLoop::instance().enterLoop(10); QVERIFY(!QTestEventLoop::instance().timeout()); - for (int a = 0; a < urls.size(); ++a) { - QVERIFY(replies.at(a)->isFinished()); - QCOMPARE(errorSpies.at(a)->count(), 1); - errorSpies.at(a)->deleteLater(); - QCOMPARE(finishedSpies.at(a)->count(), 1); - finishedSpies.at(a)->deleteLater(); - replies.at(a)->deleteLater(); + + for (size_t i = 0; i < NUrls; ++i) { + QVERIFY(replies[i]->isFinished()); + QCOMPARE(errorSpies[i]->size(), 1); + QCOMPARE(finishedSpies[i]->size(), 1); } } @@ -8420,7 +8909,7 @@ protected slots: void tst_QNetworkReply::putWithRateLimiting() { QFile reference(testDataDir + "/rfc3252.txt"); - reference.open(QIODevice::ReadOnly); + QVERIFY(reference.open(QIODevice::ReadOnly)); QByteArray data = reference.readAll(); QVERIFY(data.size() > 0); @@ -8469,8 +8958,8 @@ void tst_QNetworkReply::ioHttpSingleRedirect() QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); // Redirected and finished should be emitted exactly once - QCOMPARE(redSpy.count(), 1); - QCOMPARE(finSpy.count(), 1); + QCOMPARE(redSpy.size(), 1); + QCOMPARE(finSpy.size(), 1); // Original URL should not be changed after redirect QCOMPARE(request.url(), localhost); @@ -8516,8 +9005,8 @@ void tst_QNetworkReply::ioHttpChangeMaxRedirects() QCOMPARE(waitForFinish(reply), int(Failure)); - QCOMPARE(redSpy.count(), request.maximumRedirectsAllowed()); - QCOMPARE(spy.count(), 1); + QCOMPARE(redSpy.size(), request.maximumRedirectsAllowed()); + QCOMPARE(spy.size(), 1); QCOMPARE(reply->error(), QNetworkReply::TooManyRedirectsError); // Increase max redirects to allow successful completion @@ -8528,7 +9017,7 @@ void tst_QNetworkReply::ioHttpChangeMaxRedirects() QVERIFY2(waitForFinish(reply2) == Success, msgWaitForFinished(reply2)); - QCOMPARE(redSpy2.count(), 2); + QCOMPARE(redSpy2.size(), 2); QCOMPARE(reply2->url(), server3Url); QCOMPARE(reply2->error(), QNetworkReply::NoError); QVERIFY(validateRedirectedResponseHeaders(reply2)); @@ -8661,8 +9150,8 @@ void tst_QNetworkReply::ioHttpRedirectPolicy() 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(finishedSpy.size(), 1); + QCOMPARE(redirectSpy.size(), redirectCount); QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode); QVERIFY(validateRedirectedResponseHeaders(reply) || statusCode != 200); } @@ -8745,7 +9234,7 @@ void tst_QNetworkReply::ioHttpRedirectPolicyErrors() QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); QCOMPARE(waitForFinish(reply), int(Failure)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(reply->error(), expectedError); } @@ -8795,7 +9284,7 @@ void tst_QNetworkReply::ioHttpUserVerifiedRedirect() QSignalSpy finishedSpy(reply.data(), SIGNAL(finished())); waitForFinish(reply); - QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(finishedSpy.size(), 1); QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode); QVERIFY(validateRedirectedResponseHeaders(reply) || statusCode != 200); } @@ -8824,7 +9313,7 @@ void tst_QNetworkReply::ioHttpCookiesDuringRedirect() manager.setRedirectPolicy(oldRedirectPolicy); QVERIFY(waitForFinish(reply) == Success); - QVERIFY(target.receivedData.contains("\r\nCookie: hello=world\r\n")); + QVERIFY(target.receivedData.contains("\r\ncookie: hello=world\r\n")); QVERIFY(validateRedirectedResponseHeaders(reply)); } @@ -9386,7 +9875,7 @@ void tst_QNetworkReply::autoDeleteRepliesAttribute() QSignalSpy finishedSpy(reply, &QNetworkReply::finished); QSignalSpy destroyedSpy(reply, &QObject::destroyed); QVERIFY(finishedSpy.wait()); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); QVERIFY(destroyedSpy.wait()); } { @@ -9397,7 +9886,7 @@ void tst_QNetworkReply::autoDeleteRepliesAttribute() QSignalSpy finishedSpy(reply, &QNetworkReply::finished); QSignalSpy destroyedSpy(reply, &QObject::destroyed); QVERIFY(finishedSpy.wait()); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); QVERIFY(destroyedSpy.wait()); } // Now repeated, but without the attribute to make sure it does not get deleted automatically. @@ -9411,10 +9900,10 @@ void tst_QNetworkReply::autoDeleteRepliesAttribute() QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished); QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed); QVERIFY(finishedSpy.wait()); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); QCoreApplication::processEvents(); QCoreApplication::processEvents(); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); } { // Post @@ -9423,10 +9912,10 @@ void tst_QNetworkReply::autoDeleteRepliesAttribute() QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished); QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed); QVERIFY(finishedSpy.wait()); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); QCoreApplication::processEvents(); QCoreApplication::processEvents(); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); } } @@ -9447,7 +9936,7 @@ void tst_QNetworkReply::autoDeleteReplies() QSignalSpy finishedSpy(reply, &QNetworkReply::finished); QSignalSpy destroyedSpy(reply, &QObject::destroyed); QVERIFY(finishedSpy.wait()); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); QVERIFY(destroyedSpy.wait()); } { @@ -9457,7 +9946,7 @@ void tst_QNetworkReply::autoDeleteReplies() QSignalSpy finishedSpy(reply, &QNetworkReply::finished); QSignalSpy destroyedSpy(reply, &QObject::destroyed); QVERIFY(finishedSpy.wait()); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); QVERIFY(destroyedSpy.wait()); } // Here we repeat the test, but override the auto-deletion in the QNetworkRequest @@ -9472,10 +9961,10 @@ void tst_QNetworkReply::autoDeleteReplies() QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished); QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed); QVERIFY(finishedSpy.wait()); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); QCoreApplication::processEvents(); QCoreApplication::processEvents(); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); } { // Post @@ -9485,10 +9974,10 @@ void tst_QNetworkReply::autoDeleteReplies() QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished); QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed); QVERIFY(finishedSpy.wait()); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); QCoreApplication::processEvents(); QCoreApplication::processEvents(); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); } // Now we repeat the test with autoDeleteReplies set to false cleanup.dismiss(); @@ -9500,10 +9989,10 @@ void tst_QNetworkReply::autoDeleteReplies() QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished); QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed); QVERIFY(finishedSpy.wait()); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); QCoreApplication::processEvents(); QCoreApplication::processEvents(); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); } { // Post @@ -9512,89 +10001,71 @@ void tst_QNetworkReply::autoDeleteReplies() QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished); QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed); QVERIFY(finishedSpy.wait()); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); QCoreApplication::processEvents(); QCoreApplication::processEvents(); - QCOMPARE(destroyedSpy.count(), 0); + QCOMPARE(destroyedSpy.size(), 0); } } -void tst_QNetworkReply::getWithTimeout() +void tst_QNetworkReply::requestWithTimeout_data() { - MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false); + using Operation = QNetworkAccessManager::Operation; + QTest::addColumn<Operation>("method"); + QTest::addColumn<int>("reqInt"); + QTest::addColumn<std::chrono::milliseconds>("reqChrono"); + QTest::addColumn<int>("mgrInt"); + QTest::addColumn<std::chrono::milliseconds>("mgrChrono"); - QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); - QNetworkReplyPtr reply(manager.get(request)); - QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); - - QCOMPARE(waitForFinish(reply), int(Success)); - - QCOMPARE(spy.count(), 0); - QVERIFY(reply->error() == QNetworkReply::NoError); - - request.setTransferTimeout(1000); - server.stopTransfer = true; - - QNetworkReplyPtr reply2(manager.get(request)); - QSignalSpy spy2(reply2.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); + QTest::addRow("get_req_int") << Operation::GetOperation << 500 << 0ms << 0 << 0ms; + QTest::addRow("get_req_chrono") << Operation::GetOperation << 0 << 500ms << 0 << 0ms; + QTest::addRow("get_mgr_int") << Operation::GetOperation << 0 << 0ms << 500 << 0ms; + QTest::addRow("get_mgr_chrono") << Operation::GetOperation << 0 << 0ms << 0 << 500ms; - QCOMPARE(waitForFinish(reply2), int(Failure)); - - QCOMPARE(spy2.count(), 1); - QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError); - - request.setTransferTimeout(0); - manager.setTransferTimeout(1000); - - QNetworkReplyPtr reply3(manager.get(request)); - QSignalSpy spy3(reply3.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); - - QCOMPARE(waitForFinish(reply3), int(Failure)); - - QCOMPARE(spy3.count(), 1); - QVERIFY(reply3->error() == QNetworkReply::OperationCanceledError); - - manager.setTransferTimeout(0); + QTest::addRow("post_req_int") << Operation::PostOperation << 500 << 0ms << 0 << 0ms; + QTest::addRow("post_req_chrono") << Operation::PostOperation << 0 << 500ms << 0 << 0ms; + QTest::addRow("post_mgr_int") << Operation::PostOperation << 0 << 0ms << 500 << 0ms; + QTest::addRow("post_mgr_chrono") << Operation::PostOperation << 0 << 0ms << 0 << 500ms; } -void tst_QNetworkReply::postWithTimeout() +void tst_QNetworkReply::requestWithTimeout() { + QFETCH(QNetworkAccessManager::Operation, method); + QFETCH(int, reqInt); + QFETCH(int, mgrInt); + QFETCH(std::chrono::milliseconds, reqChrono); + QFETCH(std::chrono::milliseconds, mgrChrono); + const auto data = "some data"_ba; + // Manager instance remains between case runs => always reset it's transferTimeout to + // ensure setting its transferTimeout in this case has effect + manager.setTransferTimeout(0ms); + auto cleanup = qScopeGuard([this] { manager.setTransferTimeout(0ms); }); + MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false); + server.stopTransfer = true; QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); request.setRawHeader("Content-Type", "application/octet-stream"); - QByteArray postData("Just some nonsense"); - QNetworkReplyPtr reply(manager.post(request, postData)); - QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); - - QCOMPARE(waitForFinish(reply), int(Success)); - - QCOMPARE(spy.count(), 0); - QVERIFY(reply->error() == QNetworkReply::NoError); - - request.setTransferTimeout(1000); - server.stopTransfer = true; - - QNetworkReplyPtr reply2(manager.post(request, postData)); - QSignalSpy spy2(reply2.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); - - QCOMPARE(waitForFinish(reply2), int(Failure)); - - QCOMPARE(spy2.count(), 1); - QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError); + if (reqInt > 0) + request.setTransferTimeout(reqInt); + if (reqChrono > 0ms) + request.setTransferTimeout(reqChrono); + if (mgrInt > 0) + manager.setTransferTimeout(mgrInt); + if (mgrChrono > 0ms) + manager.setTransferTimeout(mgrChrono); - request.setTransferTimeout(0); - manager.setTransferTimeout(1000); - - QNetworkReplyPtr reply3(manager.post(request, postData)); - QSignalSpy spy3(reply3.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError))); - - QCOMPARE(waitForFinish(reply3), int(Failure)); - - QCOMPARE(spy3.count(), 1); - QVERIFY(reply3->error() == QNetworkReply::OperationCanceledError); + QNetworkReplyPtr reply; + if (method == QNetworkAccessManager::GetOperation) + reply.reset(manager.get(request)); + else if (method == QNetworkAccessManager::PostOperation) + reply.reset(manager.post(request, data)); + QVERIFY(reply); - manager.setTransferTimeout(0); + QSignalSpy spy(reply.data(), &QNetworkReply::errorOccurred); + QCOMPARE(waitForFinish(reply), int(Failure)); + QCOMPARE(spy.size(), 1); + QCOMPARE(reply->error(), QNetworkReply::OperationCanceledError); } void tst_QNetworkReply::moreActivitySignals_data() @@ -9636,13 +10107,13 @@ void tst_QNetworkReply::moreActivitySignals() QSignalSpy spy3(reply.data(), SIGNAL(metaDataChanged())); QSignalSpy spy4(reply.data(), SIGNAL(finished())); spy1.wait(); - QCOMPARE(spy1.count(), 1); + QCOMPARE(spy1.size(), 1); spy2.wait(); - QCOMPARE(spy2.count(), 1); + QCOMPARE(spy2.size(), 1); spy3.wait(); - QCOMPARE(spy3.count(), 1); + QCOMPARE(spy3.size(), 1); spy4.wait(); - QCOMPARE(spy4.count(), 1); + QCOMPARE(spy4.size(), 1); QVERIFY(reply->error() == QNetworkReply::NoError); // Second request will not send socketStartedConnecting because of keep-alive, so don't check it. QNetworkReplyPtr secondreply; @@ -9656,11 +10127,11 @@ void tst_QNetworkReply::moreActivitySignals() QSignalSpy secondspy3(secondreply.data(), SIGNAL(metaDataChanged())); QSignalSpy secondspy4(secondreply.data(), SIGNAL(finished())); secondspy2.wait(); - QCOMPARE(secondspy2.count(), 1); + QCOMPARE(secondspy2.size(), 1); secondspy3.wait(); - QCOMPARE(secondspy3.count(), 1); + QCOMPARE(secondspy3.size(), 1); secondspy4.wait(); - QCOMPARE(secondspy4.count(), 1); + QCOMPARE(secondspy4.size(), 1); QVERIFY(secondreply->error() == QNetworkReply::NoError); } @@ -9734,7 +10205,7 @@ void tst_QNetworkReply::contentEncoding() QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort()))); if (!decompress) { // This disables decompression of the received content: - request.setRawHeader("Accept-Encoding", QLatin1String("%1").arg(encoding).toLatin1()); + request.setRawHeader("accept-encoding", QLatin1String("%1").arg(encoding).toLatin1()); // This disables the zerocopy optimization request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 0); } @@ -9746,7 +10217,7 @@ void tst_QNetworkReply::contentEncoding() { // Check that we included the content encoding method in our Accept-Encoding header const QByteArray &receivedData = server.receivedData; - int start = receivedData.indexOf("Accept-Encoding"); + int start = receivedData.indexOf("accept-encoding"); QVERIFY(start != -1); int end = receivedData.indexOf("\r\n", start); QVERIFY(end != -1); @@ -10008,6 +10479,88 @@ void tst_QNetworkReply::notFoundWithCompression() QCOMPARE(reply->readAll(), expected); } +void tst_QNetworkReply::qtbug68821proxyError_data() +{ + QTest::addColumn<QString>("proxyHost"); + QTest::addColumn<QString>("scheme"); + QTest::addColumn<QNetworkReply::NetworkError>("error"); + + QTest::newRow("invalidhost+http") << "this-host-will-never-exist.qt-project.org" + << "http" << QNetworkReply::ProxyNotFoundError; + QTest::newRow("localhost+http") << "localhost" + << "http" << QNetworkReply::ProxyConnectionRefusedError; +#ifndef QT_NO_SSL + QTest::newRow("invalidhost+https") << "this-host-will-never-exist.qt-project.org" + << "https" << QNetworkReply::ProxyNotFoundError; + QTest::newRow("localhost+https") << "localhost" + << "https" << QNetworkReply::ProxyConnectionRefusedError; +#endif +} + +void tst_QNetworkReply::qtbug68821proxyError() +{ + auto getUnusedPort = []() -> std::optional<quint16> { + QTcpServer probeServer; + if (!probeServer.listen()) + return std::nullopt; + // If we can listen on it, it was unused, and hopefully is also + // still unused after we stop listening. + return probeServer.serverPort(); + }; + + auto proxyPort = getUnusedPort(); + QVERIFY(proxyPort); + + QFETCH(QString, proxyHost); + QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyHost, proxyPort.value()); + + manager.setProxy(proxy); + + QFETCH(QString, scheme); + QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(scheme + "://example.com"))); + QSignalSpy spy(reply, &QNetworkReply::errorOccurred); + QVERIFY(spy.isValid()); + + QVERIFY(spy.wait(15000)); + + QFETCH(QNetworkReply::NetworkError, error); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.at(0).at(0), error); +} + +void tst_QNetworkReply::abortAndError() +{ + const QByteArray response = + R"(HTTP/1.0 500 Internal Server Error +Content-Length: 12 +Content-Type: text/plain + +Hello World!)"_ba; + + MiniHttpServer server(response); + + QNetworkAccessManager manager; + QNetworkRequest req(QUrl("http://127.0.0.1:" + QString::number(server.serverPort()))); + std::unique_ptr<QNetworkReply> reply(manager.post(req, "my data goes here"_ba)); + QSignalSpy errorSignal(reply.get(), &QNetworkReply::errorOccurred); + QSignalSpy finishedSignal(reply.get(), &QNetworkReply::finished); + + reply->abort(); + + // We don't want to print this warning in this case because it is impossible + // for users to avoid it. + QTest::failOnWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only " + "be called once."); + // Process any signals from the http thread: + QTest::qWait(1s); + if (QTest::currentTestFailed()) + return; + + QCOMPARE(finishedSignal.count(), 1); + QCOMPARE(errorSignal.count(), 1); + QCOMPARE(reply->error(), QNetworkReply::OperationCanceledError); +} + // NOTE: This test must be last testcase in tst_qnetworkreply! void tst_QNetworkReply::parentingRepliesToTheApp() { |