Qt: Use main HTTP downloader for updater

pull/3745/head
Stenzek 4 months ago
parent 8ae5c854e5
commit fc8446de62
No known key found for this signature in database

@ -1,7 +1,8 @@
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "autoupdaterdialog.h"
#include "asynchttprequest.h"
#include "mainwindow.h"
#include "qthost.h"
#include "qtprogresscallback.h"
@ -44,9 +45,6 @@
using namespace Qt::StringLiterals;
// Interval at which HTTP requests are polled.
static constexpr u32 HTTP_POLL_INTERVAL = 10;
#if defined(_WIN32)
#include "common/windows_headers.h"
#include <shellapi.h>
@ -113,7 +111,7 @@ static constexpr const std::pair<const char*, const char*> s_update_channels[] =
LOG_CHANNEL(Host);
AutoUpdaterDialog::AutoUpdaterDialog(QWidget* const parent, Error* const error) : QDialog(parent)
AutoUpdaterDialog::AutoUpdaterDialog(QWidget* const parent) : QDialog(parent)
{
m_ui.setupUi(this);
QFont title_font(m_ui.titleLabel->font());
@ -126,25 +124,12 @@ AutoUpdaterDialog::AutoUpdaterDialog(QWidget* const parent, Error* const error)
connect(m_ui.downloadAndInstall, &QPushButton::clicked, this, &AutoUpdaterDialog::downloadUpdateClicked);
connect(m_ui.skipThisUpdate, &QPushButton::clicked, this, &AutoUpdaterDialog::skipThisUpdateClicked);
connect(m_ui.remindMeLater, &QPushButton::clicked, this, &AutoUpdaterDialog::remindMeLaterClicked);
m_http = HTTPDownloader::Create(HTTPCache::GetUserAgent(), error);
m_http_poll_timer = new QTimer(this);
m_http_poll_timer->connect(m_http_poll_timer, &QTimer::timeout, this, &AutoUpdaterDialog::httpPollTimerPoll);
}
AutoUpdaterDialog::~AutoUpdaterDialog() = default;
AutoUpdaterDialog* AutoUpdaterDialog::create(QWidget* const parent, Error* const error)
AutoUpdaterDialog::~AutoUpdaterDialog()
{
AutoUpdaterDialog* const win = new AutoUpdaterDialog(parent, error);
if (!win->m_http)
{
delete win;
return nullptr;
}
return win;
// Ensure all requests have finished.
HTTPCache::CancelRequestsForOwner(this);
}
void AutoUpdaterDialog::warnAboutUnofficialBuild()
@ -310,33 +295,14 @@ void AutoUpdaterDialog::reportError(const std::string_view msg)
msgbox->open();
}
void AutoUpdaterDialog::ensureHttpPollingActive()
{
if (m_http_poll_timer->isActive())
return;
m_http_poll_timer->setSingleShot(false);
m_http_poll_timer->setInterval(HTTP_POLL_INTERVAL);
m_http_poll_timer->start();
}
void AutoUpdaterDialog::httpPollTimerPoll()
{
m_http->PollRequests();
if (!m_http->HasAnyRequests())
{
VERBOSE_LOG("All HTTP requests done.");
m_http_poll_timer->stop();
}
}
void AutoUpdaterDialog::cancel()
{
if (m_updates_available)
return;
m_http->CancelAllRequests();
HTTPDownloader* const downloader = HTTPCache::GetDownloader();
if (downloader)
downloader->CancelRequestsForOwner(this);
}
bool AutoUpdaterDialog::handleCancelledRequest(s32 status_code)
@ -358,21 +324,22 @@ void AutoUpdaterDialog::queueUpdateCheck(bool display_errors, bool ignore_skippe
Host::CommitBaseSettingChanges();
}
ensureHttpPollingActive();
m_http->CreateRequest(
LATEST_TAG_URL, this,
[this, display_errors](s32 status_code, Error& error, std::string& content_type, std::vector<u8>& response) {
getLatestTagComplete(status_code, error, response, display_errors);
});
AsyncHTTPRequest* const req = new AsyncHTTPRequest();
connect(req, &AsyncHTTPRequest::requestComplete, this,
[this, display_errors](s32 status_code, Error& error, std::string& content_type, std::vector<u8>& response) {
getLatestTagComplete(status_code, error, response, display_errors);
});
req->get(LATEST_TAG_URL, this);
}
void AutoUpdaterDialog::queueGetLatestRelease()
{
ensureHttpPollingActive();
std::string url = fmt::format(LATEST_RELEASE_URL, getCurrentUpdateTag());
m_http->CreateRequest(std::move(url), this,
std::bind(&AutoUpdaterDialog::getLatestReleaseComplete, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_4));
AsyncHTTPRequest* const req = new AsyncHTTPRequest();
connect(req, &AsyncHTTPRequest::requestComplete, this,
[this](s32 status_code, Error& error, std::string& content_type, std::vector<u8>& response) {
getLatestReleaseComplete(status_code, error, response);
});
req->get(fmt::format(LATEST_RELEASE_URL, getCurrentUpdateTag()), this);
}
void AutoUpdaterDialog::getLatestTagComplete(s32 status_code, Error& error, std::vector<u8>& response,
@ -520,11 +487,12 @@ void AutoUpdaterDialog::getLatestReleaseComplete(s32 status_code, Error& error,
void AutoUpdaterDialog::queueGetChanges()
{
ensureHttpPollingActive();
std::string url = fmt::format(CHANGES_URL, g_scm_hash_str, getCurrentUpdateTag());
m_http->CreateRequest(std::move(url), this,
std::bind(&AutoUpdaterDialog::getChangesComplete, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_4));
AsyncHTTPRequest* const req = new AsyncHTTPRequest();
connect(req, &AsyncHTTPRequest::requestComplete, this,
[this](s32 status_code, Error& error, std::string& content_type, std::vector<u8>& response) {
getChangesComplete(status_code, error, response);
});
req->get(fmt::format(CHANGES_URL, g_scm_hash_str, getCurrentUpdateTag()), this);
}
void AutoUpdaterDialog::getChangesComplete(s32 status_code, Error& error, std::vector<u8>& response)
@ -607,49 +575,51 @@ void AutoUpdaterDialog::downloadUpdateClicked()
m_ui.downloadButtonBox->button(QDialogButtonBox::Cancel));
m_download_progress_callback->SetStatusText(TRANSLATE_SV("AutoUpdaterWindow", "Downloading Update..."));
ensureHttpPollingActive();
m_http->CreateRequest(
m_download_url.toStdString(), this,
[this](s32 status_code, Error& error, std::string&, std::vector<u8>& response) {
m_download_progress_callback->SetStatusText(TRANSLATE_SV("AutoUpdaterWindow", "Processing Update..."));
m_download_progress_callback->SetProgressRange(1);
m_download_progress_callback->SetProgressValue(1);
DebugAssert(m_download_progress_callback);
delete m_download_progress_callback;
m_download_progress_callback = nullptr;
if (status_code == HTTPDownloader::HTTP_STATUS_CANCELLED)
{
setDownloadSectionVisibility(false);
return;
}
AsyncHTTPRequest* const req = new AsyncHTTPRequest();
connect(req, &AsyncHTTPRequest::requestComplete, this,
[this](s32 status_code, Error& error, std::string&, std::vector<u8>& response) {
downloadUpdateComplete(status_code, error, response);
});
req->get(m_download_url.toStdString(), this, m_download_progress_callback);
}
if (status_code != HTTPDownloader::HTTP_STATUS_OK)
{
reportError(fmt::format("Download failed: {}", error.GetDescription()));
setDownloadSectionVisibility(false);
return;
}
void AutoUpdaterDialog::downloadUpdateComplete(s32 status_code, Error& error, std::vector<u8>& response)
{
DebugAssert(m_download_progress_callback);
m_download_progress_callback->SetState(TRANSLATE_SV("AutoUpdaterWindow", "Processing Update..."), 1, 1);
delete m_download_progress_callback;
m_download_progress_callback = nullptr;
if (response.empty())
{
reportError("Download failed: Update is empty");
setDownloadSectionVisibility(false);
return;
}
if (status_code == HTTPDownloader::HTTP_STATUS_CANCELLED)
{
setDownloadSectionVisibility(false);
return;
}
if (processUpdate(response))
{
// updater started, request exit. can't do it immediately as closing the main window will delete us.
QMetaObject::invokeMethod(g_main_window, &MainWindow::requestExit, Qt::QueuedConnection, false);
}
else
{
// allow user to try again
setDownloadSectionVisibility(false);
}
},
m_download_progress_callback);
if (status_code != HTTPDownloader::HTTP_STATUS_OK)
{
reportError(fmt::format("Download failed: {}", error.GetDescription()));
setDownloadSectionVisibility(false);
return;
}
if (response.empty())
{
reportError("Download failed: Update is empty");
setDownloadSectionVisibility(false);
return;
}
if (processUpdate(response))
{
// updater started, request exit. can't do it immediately as closing the main window will delete us.
QMetaObject::invokeMethod(g_main_window, &MainWindow::requestExit, Qt::QueuedConnection, false);
}
else
{
// allow user to try again
setDownloadSectionVisibility(false);
}
}
bool AutoUpdaterDialog::updateNeeded() const

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
@ -7,7 +7,6 @@
#include "common/types.h"
#include <QtCore/QTimer>
#include <string>
#include <string_view>
#include <vector>
@ -23,10 +22,9 @@ class AutoUpdaterDialog final : public QDialog
Q_OBJECT
public:
AutoUpdaterDialog(QWidget* const parent);
~AutoUpdaterDialog();
static AutoUpdaterDialog* create(QWidget* const parent, Error* const error);
ALWAYS_INLINE bool areUpdatesAvailable() const { return m_updates_available; }
void queueUpdateCheck(bool display_errors, bool ignore_skipped_updates);
@ -56,13 +54,9 @@ protected:
void closeEvent(QCloseEvent* event) override;
private:
AutoUpdaterDialog(QWidget* const parent, Error* const error);
void setDownloadSectionVisibility(bool visible);
void reportError(const std::string_view msg);
void ensureHttpPollingActive();
void httpPollTimerPoll();
bool handleCancelledRequest(s32 status_code);
void downloadUpdateClicked();
@ -77,6 +71,7 @@ private:
void queueGetChanges();
void getChangesComplete(s32 status_code, Error& error, std::vector<u8>& response);
void downloadUpdateComplete(s32 status_code, Error& error, std::vector<u8>& response);
bool processUpdate(const std::vector<u8>& update_data);
#ifdef _WIN32
@ -89,8 +84,6 @@ private:
Ui::AutoUpdaterDialog m_ui;
std::unique_ptr<HTTPDownloader> m_http;
QTimer* m_http_poll_timer = nullptr;
QtProgressCallback* m_download_progress_callback = nullptr;
QString m_latest_sha;
QString m_download_url;

@ -3488,19 +3488,7 @@ AutoUpdaterDialog* MainWindow::createAutoUpdaterDialog(QWidget* parent, bool dis
return nullptr;
}
Error error;
m_auto_updater_dialog = AutoUpdaterDialog::create(parent, &error);
if (!m_auto_updater_dialog)
{
if (display_message)
{
QtUtils::AsyncMessageBox(
parent, QMessageBox::Critical, "Error"_L1,
QStringLiteral("Failed to create auto updater: %1").arg(QString::fromStdString(error.GetDescription())));
}
return nullptr;
}
m_auto_updater_dialog = new AutoUpdaterDialog(parent);
// display status message indicating check is in progress
// technically this could conflict with the game list refresh, but this is only for manual update checks.

Loading…
Cancel
Save