diff --git a/src/libsync/accessmanager.cpp b/src/libsync/accessmanager.cpp index 88adc6a5dfbf9..35630feb9dee7 100644 --- a/src/libsync/accessmanager.cpp +++ b/src/libsync/accessmanager.cpp @@ -82,10 +82,12 @@ QNetworkReply *AccessManager::createRequest(QNetworkAccessManager::Operation op, } #endif - // We handle redirects ourselves in AbstractNetworkJob::slotFinished - // Qt's automatic handling of redirects will transmit all set headers from the original - // request again, including e.g. `Authorization`. - newRequest.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy); + if (!newRequest.attribute(QNetworkRequest::RedirectPolicyAttribute).isValid()) { + // We handle redirects ourselves in AbstractNetworkJob::slotFinished + // Qt's automatic handling of redirects will transmit all set headers from the original + // request again, including e.g. `Authorization`. + newRequest.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy); + } const auto reply = QNetworkAccessManager::createRequest(op, newRequest, outgoingData); HttpLogger::logRequest(reply, op, outgoingData); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d7e0ecd7f9d65..66b340bed2d5a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,11 +30,16 @@ set_target_properties(testutils PROPERTIES FOLDER Tests) nextcloud_add_test(NextcloudPropagator) if(Qt${QT_VERSION_MAJOR}HttpServer_FOUND) target_compile_definitions(NextcloudPropagatorTest PRIVATE HAVE_QHTTPSERVER=1) - target_link_libraries(NextcloudPropagatorTest PRIVATE Qt6::HttpServer) + target_link_libraries(NextcloudPropagatorTest PRIVATE Qt::HttpServer) endif() IF(BUILD_UPDATER) nextcloud_add_test(Updater) + + if(Qt${QT_VERSION_MAJOR}HttpServer_FOUND) + target_compile_definitions(UpdaterTest PRIVATE HAVE_QHTTPSERVER=1) + target_link_libraries(UpdaterTest PRIVATE Qt::HttpServer) + endif() endif() nextcloud_add_test(NetrcParser) diff --git a/test/testupdater.cpp b/test/testupdater.cpp index a0c4f2dca3a32..7ea14247301ef 100644 --- a/test/testupdater.cpp +++ b/test/testupdater.cpp @@ -10,9 +10,19 @@ #include +#include "common/filesystembase.h" #include "updater/updater.h" #include "updater/ocupdater.h" +#include "configfile.h" #include "logger.h" +#include "filesystem.h" + +using namespace Qt::StringLiterals; + +#ifdef HAVE_QHTTPSERVER +#include +#include +#endif using namespace OCC; @@ -41,7 +51,71 @@ private slots: QVERIFY(currVersion < highVersion); } +#ifdef HAVE_QHTTPSERVER + void testUpdaterDownloadRedirect() + { + QTemporaryDir tempDir; + ConfigFile::setConfDir(tempDir.path()); // we don't want to pollute the user's config file + QVERIFY(tempDir.isValid()); + QDir dir(tempDir.path()); + + // set up a download server that provides the version info and redirects the download request to e.g. some object storage + QHttpServer httpServer; + httpServer.route("/updateinfo.xml", [](const QHttpServerRequest &request, QHttpServerResponder &responder) -> void { + auto downloadTarget = request.url(); + downloadTarget.setPath("/Nextcloud.msi"); + qInfo() << "redirecting to" << downloadTarget; + + QHttpHeaders headers; + headers.append(QHttpHeaders::WellKnownHeader::ContentType, "application/xml"_ba); + + auto xmlResponse = "\n600.0.0Nextcloud Client 600.0.0"_ba; + xmlResponse.append(downloadTarget.toEncoded()); + xmlResponse.append("https://nextcloud.com/install"_ba); + + responder.write(xmlResponse, headers); + }); + httpServer.route("/Nextcloud.msi", [](QHttpServerResponder &responder) -> void { + QHttpHeaders headers; + headers.append(QHttpHeaders::WellKnownHeader::Location, "/storage/blob/42?signature=1234abcd&signatureVersion=2026-01-21"_ba); + responder.write(""_ba, headers, QHttpServerResponder::StatusCode::Found); + }); + + bool redirectHit = false; + httpServer.route("/storage/blob/42", [&redirectHit](QHttpServerResponder &responder) -> void { + QHttpHeaders headers; + headers.append(QHttpHeaders::WellKnownHeader::ContentType, "application/octet-stream"_ba); + redirectHit = true; + + responder.write("This would be the installer"_ba, headers); + }); + + QTcpServer tcpServer; + QVERIFY(tcpServer.listen(QHostAddress::LocalHost)); + QVERIFY(httpServer.bind(&tcpServer)); + const QString baseUrl = "http://%1:%2"_L1.arg(tcpServer.serverAddress().toString(), QString::number(tcpServer.serverPort())); + qInfo() << "Listening on" << baseUrl; + + NSISUpdater updater(QUrl("%1/updateinfo.xml"_L1.arg(baseUrl))); + QSignalSpy downloadAvailableSpy(&updater, &OCUpdater::newUpdateAvailable); + updater.checkForUpdate(); + downloadAvailableSpy.wait(); + QCOMPARE(downloadAvailableSpy.size(), 1); + QVERIFY(redirectHit); + + ConfigFile cfg; + QSettings settings(cfg.configFile(), QSettings::IniFormat); + const auto downloadedUpdateFilePath = settings.value("Updater/updateAvailable"_L1).toString(); // anonymous const in ocupdater.cpp + const auto expectedUpdateFilePath = FileSystem::joinPath(cfg.configPath(), "Nextcloud.msi"); + QCOMPARE(downloadedUpdateFilePath, expectedUpdateFilePath); + QFile updateFile(expectedUpdateFilePath); + QVERIFY(updateFile.open(QIODevice::ReadOnly)); + const auto updateContents = updateFile.readAll(); + updateFile.close(); + QCOMPARE(updateContents, "This would be the installer"_ba); + } +#endif }; -QTEST_APPLESS_MAIN(TestUpdater) +QTEST_GUILESS_MAIN(TestUpdater) #include "testupdater.moc"