QHttp: Fix handling of redirect loaded from cache

In some situations when loading a redirect from cache which would lead
to a real request we would not emit the finished() signal because the
replyFinished function has a pre-condition that the response did
not originate from the cache.
However, after the initial redirect was loaded from the cache we never
unset the 'loadingFromCache' boolean, so it was still true after the
request had been made to the real target.

Pick-to: 6.2 6.3 5.15
Change-Id: I015a2ebae4af4bd17392182c3951e875a7b353c4
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Mårten Nordheim 2022-04-21 17:32:59 +02:00
parent d642c16fe7
commit 41d217829c
2 changed files with 60 additions and 0 deletions

View File

@ -712,6 +712,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
httpRequest.setRedirectPolicy(redirectPolicy);
httpRequest.setPriority(convert(newHttpRequest.priority()));
loadingFromCache = false;
switch (operation) {
case QNetworkAccessManager::GetOperation:

View File

@ -500,6 +500,7 @@ private Q_SLOTS:
void ioHttpCookiesDuringRedirect();
void ioHttpRedirect_data();
void ioHttpRedirect();
void ioHttpRedirectWithCache();
void ioHttpRedirectFromLocalToRemote();
void ioHttpRedirectPostPut_data();
void ioHttpRedirectPostPut();
@ -8874,6 +8875,64 @@ void tst_QNetworkReply::ioHttpRedirect()
QVERIFY(validateRedirectedResponseHeaders(reply));
}
/*
Test that, if we load a redirect from cache, we don't treat the request to
the destination of the redirect as a redirect.
If it was treated as a redirect the finished() signal was never emitted!
*/
void tst_QNetworkReply::ioHttpRedirectWithCache()
{
// Disallow caching the result so that the second request must also send the request
QByteArray http200ResponseNoCache = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Cache-Control: no-cache\r\n"
"\r\nHello";
MiniHttpServer target(http200ResponseNoCache, false);
QUrl targetUrl("http://localhost/");
targetUrl.setPort(target.serverPort());
// A cache-able redirect reply
QString redirectReply = QStringLiteral("HTTP/1.1 308\r\n"
"Content-Type: text/plain\r\n"
"location: %1\r\n"
"Cache-Control: max-age=3600\r\n"
"\r\nYou're being redirected").arg(targetUrl.toString());
MiniHttpServer redirectServer(redirectReply.toLatin1(), false);
QUrl url("http://localhost/");
url.setPort(redirectServer.serverPort());
QTemporaryDir tempDir(QDir::tempPath() + "/tmp_cache_28035");
QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString()));
tempDir.setAutoRemove(true);
QNetworkDiskCache *diskCache = new QNetworkDiskCache();
diskCache->setCacheDirectory(tempDir.path());
// Manager takes ownership of the cache:
manager.setCache(diskCache);
QCOMPARE(diskCache->cacheSize(), 0);
// Send the first request, we end up caching the redirect reply
QNetworkRequest request(url);
QNetworkReplyPtr reply(manager.get(request));
QCOMPARE(waitForFinish(reply), int(Success));
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QVERIFY(validateRedirectedResponseHeaders(reply));
QVERIFY(diskCache->cacheSize() != 0);
// Now for the second request, we will use the cache, and we test that the finished()
// signal is still emitted.
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
reply.reset(manager.get(request));
QCOMPARE(waitForFinish(reply), int(Success));
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QVERIFY(validateRedirectedResponseHeaders(reply));
}
void tst_QNetworkReply::ioHttpRedirectFromLocalToRemote()
{
QUrl targetUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt");