From 518c7e61259febd7c4af130405e69a9dbf6a5eb2 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Sun, 25 Oct 2026 00:33:43 +0200 Subject: [PATCH] [Network] Add a configured QNetworkAccessManager factory (#7628) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Network] Add a configured QNetworkAccessManager factory Every HTTP caller built its own QNetworkAccessManager, so the system proxy and a transfer timeout were easy to omit. Add NetworkManagerFactory::create() as the single place that applies the process-wide proxy configuration and a transfer timeout, and use it at all 14 construction sites. Per-site settings such as disk caches and manual redirect policies stay with their callers. * [Tests] Link libcockatrice_network into the tests that compile its headers release_channel.cpp and card_picture_loader_worker.cpp now include libcockatrice/network/network_manager.h, but the test targets that compile them only linked libcockatrice_settings, so the include directory never reached them and every platform failed with "file not found": loader_local_matching_test and picture_loader_benchmark_test. libcockatrice_network is an INTERFACE library, so linking it costs these targets nothing but hands over the include path. --------- Co-authored-by: Lukas BrĂ¼bach --- .../interfaces/deck_stats_interface.cpp | 3 +- .../interfaces/tapped_out_interface.cpp | 3 +- .../spoiler_background_updater.cpp | 3 +- .../network/update/client/release_channel.cpp | 3 +- .../update/client/update_downloader.cpp | 3 +- .../card_picture_loader_worker.cpp | 4 +- .../dialogs/dlg_load_deck_from_website.cpp | 3 +- .../widgets/server/handle_public_servers.cpp | 3 +- ..._response_deck_listings_display_widget.cpp | 4 +- .../tabs/api/archidekt/tab_archidekt.cpp | 4 +- .../handle_commander_brackets.cpp | 3 +- .../widgets/tabs/api/edhrec/tab_edhrec.cpp | 4 +- .../tabs/api/edhrec/tab_edhrec_main.cpp | 4 +- .../libcockatrice/network/network_manager.h | 38 +++++++++++++++++++ oracle/src/oraclewizard.cpp | 3 +- tests/CMakeLists.txt | 1 + tests/picture_loader_benchmark/CMakeLists.txt | 3 +- 17 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 libcockatrice_network/libcockatrice/network/network_manager.h diff --git a/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp b/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp index d5cfdbbc9..508f0bdc9 100644 --- a/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp +++ b/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp @@ -20,11 +20,12 @@ #include #include #include +#include #include DeckStatsInterface::DeckStatsInterface(QObject *parent) : QObject(parent) { - manager = new QNetworkAccessManager(this); + manager = NetworkManagerFactory::create(this); connect(manager, &QNetworkAccessManager::finished, this, &DeckStatsInterface::queryFinished); } diff --git a/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp b/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp index 30044c2b6..34dd03765 100644 --- a/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp +++ b/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp @@ -26,11 +26,12 @@ #include #include #include +#include #include TappedOutInterface::TappedOutInterface(QObject *parent) : QObject(parent) { - manager = new QNetworkAccessManager(this); + manager = NetworkManagerFactory::create(this); connect(manager, &QNetworkAccessManager::finished, this, &TappedOutInterface::queryFinished); } diff --git a/cockatrice/src/client/network/update/card_spoiler/spoiler_background_updater.cpp b/cockatrice/src/client/network/update/card_spoiler/spoiler_background_updater.cpp index 192029b23..62668c4d9 100644 --- a/cockatrice/src/client/network/update/card_spoiler/spoiler_background_updater.cpp +++ b/cockatrice/src/client/network/update/card_spoiler/spoiler_background_updater.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,7 @@ void SpoilerBackgroundUpdater::startSpoilerDownloadProcess(QString url, bool sav void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults) { - auto *nam = new QNetworkAccessManager(this); + auto *nam = NetworkManagerFactory::create(this); auto request = QNetworkRequest(url); request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING)); QNetworkReply *reply = nam->get(request); diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index 45c503623..ce80a2a76 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -62,7 +63,7 @@ std::optional getProductVersionMajor() } // namespace -ReleaseChannel::ReleaseChannel() : netMan(new QNetworkAccessManager(this)), response(nullptr), lastRelease(nullptr) +ReleaseChannel::ReleaseChannel() : netMan(NetworkManagerFactory::create(this)), response(nullptr), lastRelease(nullptr) { } diff --git a/cockatrice/src/client/network/update/client/update_downloader.cpp b/cockatrice/src/client/network/update/client/update_downloader.cpp index c86972d60..8868876fa 100644 --- a/cockatrice/src/client/network/update/client/update_downloader.cpp +++ b/cockatrice/src/client/network/update/client/update_downloader.cpp @@ -9,10 +9,11 @@ #include #include #include +#include UpdateDownloader::UpdateDownloader(QObject *parent) : QObject(parent), response(nullptr) { - netMan = new QNetworkAccessManager(this); + netMan = NetworkManagerFactory::create(this); } void UpdateDownloader::beginDownload(QUrl downloadUrl) diff --git a/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp b/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp index 150b4c544..3ab5eb684 100644 --- a/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp +++ b/cockatrice/src/interface/card_picture_loader/card_picture_loader_worker.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -46,11 +47,10 @@ CardPictureLoaderWorker::CardPictureLoaderWorker() : QObject(nullptr), picDownload(SettingsCache::instance().downloads().getPicDownload()), hostRequestLimits(SettingsCache::instance().downloads().getHostRequestLimits()) { - networkManager = new QNetworkAccessManager(this); + networkManager = NetworkManagerFactory::create(this); // We need a timeout to ensure requests don't hang indefinitely in case of // cache corruption, see related Qt bug: https://bugreports.qt.io/browse/QTBUG-111397 // Use Qt's default timeout (30s, as of 2023-02-22) - networkManager->setTransferTimeout(); cache = new QNetworkDiskCache(this); cache->setCacheDirectory(SettingsCache::instance().getNetworkCachePath()); cache->setMaximumCacheSize(1024L * 1024L * diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp index 88b7e2bb3..1c1ada302 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -34,7 +35,7 @@ class QWidget; DlgLoadDeckFromWebsite::DlgLoadDeckFromWebsite(QWidget *parent) : QDialog(parent) { - nam = new QNetworkAccessManager(this); + nam = NetworkManagerFactory::create(this); layout = new QVBoxLayout(this); setLayout(layout); diff --git a/cockatrice/src/interface/widgets/server/handle_public_servers.cpp b/cockatrice/src/interface/widgets/server/handle_public_servers.cpp index 08b7837dd..1c0cbf962 100644 --- a/cockatrice/src/interface/widgets/server/handle_public_servers.cpp +++ b/cockatrice/src/interface/widgets/server/handle_public_servers.cpp @@ -13,13 +13,14 @@ #include #include #include +#include #include #include #define PUBLIC_SERVERS_JSON "https://cockatrice.github.io/public-servers.json" HandlePublicServers::HandlePublicServers(QObject *parent) - : QObject(parent), nam(new QNetworkAccessManager(this)), reply(nullptr) + : QObject(parent), nam(NetworkManagerFactory::create(this)), reply(nullptr) { } diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp index 81619bdcc..f8bf3c0dc 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -27,8 +28,7 @@ ArchidektApiResponseDeckListingsDisplayWidget::ArchidektApiResponseDeckListingsD cardSizeSlider->enableCtrlScrollResize(flowWidget); - imageNetworkManager = new QNetworkAccessManager(this); - imageNetworkManager->setTransferTimeout(); // Use Qt's default timeout + imageNetworkManager = NetworkManagerFactory::create(this); imageNetworkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); // Add widgets for deck listings diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp index 60bf00c0a..d157f75cb 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/tab_archidekt.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -58,8 +59,7 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor), currentPage(1), isLoadingMore(false), isListMode(true) { // Initialize network - networkManager = new QNetworkAccessManager(this); - networkManager->setTransferTimeout(); + networkManager = NetworkManagerFactory::create(this); networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); connect(networkManager, &QNetworkAccessManager::finished, this, &TabArchidekt::processApiJson); diff --git a/cockatrice/src/interface/widgets/tabs/api/commander_spellbook/handle_commander_brackets.cpp b/cockatrice/src/interface/widgets/tabs/api/commander_spellbook/handle_commander_brackets.cpp index 22200ffa2..407f53e99 100644 --- a/cockatrice/src/interface/widgets/tabs/api/commander_spellbook/handle_commander_brackets.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/commander_spellbook/handle_commander_brackets.cpp @@ -12,12 +12,13 @@ #include #include #include +#include #include static const QUrl COMMANDER_BRACKET_JSON_URL(QStringLiteral("https://cockatrice.github.io/commander-brackets.json")); HandleCommanderBrackets::HandleCommanderBrackets(QObject *parent) - : QObject(parent), nam(new QNetworkAccessManager(this)), reply(nullptr) + : QObject(parent), nam(NetworkManagerFactory::create(this)), reply(nullptr) { } diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp index 3aa395126..9dd0cca03 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec.cpp @@ -18,14 +18,14 @@ #include #include #include +#include #include class TabSupervisor; TabEdhRec::TabEdhRec(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) { - networkManager = new QNetworkAccessManager(this); - networkManager->setTransferTimeout(); // Use Qt's default timeout + networkManager = NetworkManagerFactory::create(this); networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); connect(networkManager, &QNetworkAccessManager::finished, this, &TabEdhRec::processApiJson); } diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp index ca575dc02..741a1e85f 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/tab_edhrec_main.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -44,8 +45,7 @@ static bool canBeCommander(const CardInfoPtr &cardInfo) TabEdhRecMain::TabEdhRecMain(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) { - networkManager = new QNetworkAccessManager(this); - networkManager->setTransferTimeout(); // Use Qt's default timeout + networkManager = NetworkManagerFactory::create(this); networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy); connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(processApiJson(QNetworkReply *))); diff --git a/libcockatrice_network/libcockatrice/network/network_manager.h b/libcockatrice_network/libcockatrice/network/network_manager.h new file mode 100644 index 000000000..bb20e1315 --- /dev/null +++ b/libcockatrice_network/libcockatrice/network/network_manager.h @@ -0,0 +1,38 @@ +#ifndef COCKATRICE_NETWORK_NETWORK_MANAGER_H +#define COCKATRICE_NETWORK_NETWORK_MANAGER_H + +#include +#include +#include + +/** + * @file network_manager.h + * @ingroup Network + * @brief Creates QNetworkAccessManagers with the application-wide baseline. + * + * Every manager in the client should start from NetworkManagerFactory::create() + * so features that apply to all HTTP traffic (the system proxy, a transfer + * timeout) cannot be forgotten in one of the many call sites. Feature-specific + * setup such as a disk cache or a manual redirect policy stays with the caller. + */ +namespace NetworkManagerFactory +{ +/** + * @brief Returns a QNetworkAccessManager configured for Cockatrice. + * + * The manager is parented to @p parent. The system proxy is enabled once, + * process-wide, the first time this function is called. Transfers use Qt's + * default timeout instead of waiting forever. + */ +inline QNetworkAccessManager *create(QObject *parent = nullptr) +{ + static std::once_flag systemProxyFlag; + std::call_once(systemProxyFlag, [] { QNetworkProxyFactory::setUseSystemConfiguration(true); }); + + auto *manager = new QNetworkAccessManager(parent); + manager->setTransferTimeout(); + return manager; +} +} // namespace NetworkManagerFactory + +#endif // COCKATRICE_NETWORK_NETWORK_MANAGER_H diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index d2b3a71a4..47afecc06 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -53,7 +54,7 @@ OracleWizard::OracleWizard(QWidget *parent) : QWizard(parent) // foreignData for any other language is never imported. importer->setCardLang(SettingsCache::instance().cardsDisplay().getCardLang()); - nam = new QNetworkAccessManager(this); + nam = NetworkManagerFactory::create(this); QList pages; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5420c07f5..e2c3252ee 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -369,6 +369,7 @@ cockatrice_add_test( ${VERSION_STRING_CPP} loader_local_matching_test.cpp LIBS + libcockatrice_network libcockatrice_settings INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/cockatrice/src diff --git a/tests/picture_loader_benchmark/CMakeLists.txt b/tests/picture_loader_benchmark/CMakeLists.txt index 0684f58f4..9b8d15999 100644 --- a/tests/picture_loader_benchmark/CMakeLists.txt +++ b/tests/picture_loader_benchmark/CMakeLists.txt @@ -15,8 +15,9 @@ cockatrice_add_manual_test( settings_cache_mock.cpp LIBS libcockatrice_card - libcockatrice_settings libcockatrice_interfaces + libcockatrice_network + libcockatrice_settings libcockatrice_utility INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/cockatrice/src