From 1067ebe9e6a6fc72052bb64561ab9106d7065b7a Mon Sep 17 00:00:00 2001
From: Stenzek
Date: Mon, 21 Sep 2026 22:08:16 +1000
Subject: [PATCH] HTTPDownloader: Use string view and move semantics for
callback error
---
src/core/achievements.cpp | 18 ++++-----
src/core/game_list.cpp | 9 +++--
src/duckstation-qt/asynchttprequest.cpp | 19 +++++-----
src/duckstation-qt/asynchttprequest.h | 9 +++--
src/duckstation-qt/autoupdaterdialog.cpp | 47 ++++++++++++------------
src/duckstation-qt/autoupdaterdialog.h | 9 +++--
src/duckstation-qt/qthost.cpp | 5 ++-
src/util/http_cache.cpp | 19 +++++-----
src/util/http_downloader.cpp | 40 +++++++++-----------
src/util/http_downloader.h | 12 +++---
10 files changed, 94 insertions(+), 93 deletions(-)
diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp
index c49be63a4..8deb14ffc 100644
--- a/src/core/achievements.cpp
+++ b/src/core/achievements.cpp
@@ -833,16 +833,16 @@ uint32_t Achievements::ClientReadMemory(uint32_t address, uint8_t* buffer, uint3
void Achievements::ClientServerCall(const rc_api_request_t* request, rc_client_server_callback_t callback,
void* callback_data, rc_client_t* client)
{
- HTTPDownloader::RequestCallback hd_callback = [callback, callback_data](s32 status_code, Error& error,
- std::string& content_type,
- HTTPDownloader::RequestData& data) {
- if (status_code != HTTPDownloader::HTTP_STATUS_OK)
- ERROR_LOG("Server call failed: {}", error.GetDescription());
+ HTTPDownloader::RequestCallback hd_callback =
+ [callback, callback_data](s32 status_code, std::string_view error_message, std::string_view content_type,
+ HTTPDownloader::RequestData data) {
+ if (status_code != HTTPDownloader::HTTP_STATUS_OK)
+ ERROR_LOG("Server call failed: {}", error_message);
- const rc_api_server_response_t rr = MakeRCAPIServerResponse(status_code, data);
- const auto lock = GetLock();
- callback(&rr, callback_data);
- };
+ const rc_api_server_response_t rr = MakeRCAPIServerResponse(status_code, data);
+ const auto lock = GetLock();
+ callback(&rr, callback_data);
+ };
const std::array headers = {s_state.http_user_agent_header.c_str()};
if (request->post_data)
diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp
index b77627bde..a79a30558 100644
--- a/src/core/game_list.cpp
+++ b/src/core/game_list.cpp
@@ -1841,11 +1841,12 @@ bool GameList::DownloadCovers(const std::vector& url_templates, boo
std::string filename = Path::URLDecode(url);
HTTPDownloader::CreateRequest(
std::move(url), &s_state,
- [use_serial, &save_callback, entry_path = std::move(entry_path), filename = std::move(filename)](
- s32 status_code, Error& error, std::string& content_type, HTTPDownloader::RequestData& data) {
+ [use_serial, &save_callback, entry_path = std::move(entry_path),
+ filename = std::move(filename)](s32 status_code, std::string_view error_message, std::string_view content_type,
+ HTTPDownloader::RequestData data) {
if (status_code != HTTPDownloader::HTTP_STATUS_OK || data.empty())
{
- ERROR_LOG("Download for {} failed: {}", Path::GetFileName(filename), error.GetDescription());
+ ERROR_LOG("Download for {} failed: {}", Path::GetFileName(filename), error_message);
return;
}
@@ -1856,8 +1857,8 @@ bool GameList::DownloadCovers(const std::vector& url_templates, boo
// prefer the content type from the response for the extension
// otherwise, if it's missing, and the request didn't have an extension.. fall back to jpegs.
+ const std::string_view content_type_extension(HTTPDownloader::GetExtensionForContentType(content_type));
std::string template_filename;
- std::string content_type_extension(HTTPDownloader::GetExtensionForContentType(content_type));
// don't treat the domain name as an extension..
const std::string::size_type last_slash = filename.find('/');
diff --git a/src/duckstation-qt/asynchttprequest.cpp b/src/duckstation-qt/asynchttprequest.cpp
index 7094765d0..1faf064c8 100644
--- a/src/duckstation-qt/asynchttprequest.cpp
+++ b/src/duckstation-qt/asynchttprequest.cpp
@@ -27,8 +27,8 @@ void AsyncHTTPRequest::get(std::string url, const void* owner, ProgressCallback*
{
HTTPDownloader::CreateRequest(
std::move(url), owner,
- [this](s32 status_code, Error& error, std::string& content_type, HTTPDownloader::RequestData& data) {
- handleResponse(status_code, error, content_type, data);
+ [this](s32 status_code, std::string_view error, std::string_view content_type, HTTPDownloader::RequestData data) {
+ handleResponse(status_code, error, content_type, std::move(data));
},
progress, additional_headers, timeout_seconds);
}
@@ -40,24 +40,25 @@ void AsyncHTTPRequest::post(std::string url, std::string post_data, const void*
{
HTTPDownloader::CreatePostRequest(
std::move(url), std::move(post_data), owner,
- [this](s32 status_code, Error& error, std::string& content_type, HTTPDownloader::RequestData& data) {
- handleResponse(status_code, error, content_type, data);
+ [this](s32 status_code, std::string_view error, std::string_view content_type, HTTPDownloader::RequestData data) {
+ handleResponse(status_code, error, content_type, std::move(data));
},
progress, additional_headers, timeout_seconds);
}
-ALWAYS_INLINE_RELEASE void AsyncHTTPRequest::handleResponse(s32 status_code, Error& error, std::string& content_type,
- HTTPDownloader::RequestData& data)
+ALWAYS_INLINE_RELEASE void AsyncHTTPRequest::handleResponse(s32 status_code, const std::string_view& error_message,
+ const std::string_view& content_type,
+ HTTPDownloader::RequestData&& data)
{
m_status_code = status_code;
- m_error = std::move(error);
- m_content_type = std::move(content_type);
+ m_error_message = error_message;
+ m_content_type = content_type;
m_data = std::move(data);
QMetaObject::invokeMethod(this, &AsyncHTTPRequest::finishRequest, Qt::QueuedConnection);
}
void AsyncHTTPRequest::finishRequest()
{
- emit requestComplete(m_status_code, m_error, m_content_type, m_data);
+ emit requestComplete(m_status_code, m_error_message, m_content_type, m_data);
deleteLater();
}
diff --git a/src/duckstation-qt/asynchttprequest.h b/src/duckstation-qt/asynchttprequest.h
index c89368360..c5598bd03 100644
--- a/src/duckstation-qt/asynchttprequest.h
+++ b/src/duckstation-qt/asynchttprequest.h
@@ -28,15 +28,16 @@ public:
HTTPDownloader::HeaderList additional_headers = {}, std::optional timeout_seconds = {});
Q_SIGNALS:
- void requestComplete(qint32 status_code, Error& error_message, std::string& content_type,
- HTTPDownloader::RequestData& data);
+ void requestComplete(qint32 status_code, const std::string& error_message, const std::string& content_type,
+ const HTTPDownloader::RequestData& data);
private:
- void handleResponse(s32 status_code, Error& error, std::string& content_type, HTTPDownloader::RequestData& data);
+ void handleResponse(s32 status_code, const std::string_view& error_message, const std::string_view& content_type,
+ HTTPDownloader::RequestData&& data);
void finishRequest();
s32 m_status_code = HTTPDownloader::HTTP_STATUS_ERROR;
- Error m_error;
+ std::string m_error_message;
std::string m_content_type;
HTTPDownloader::RequestData m_data;
};
diff --git a/src/duckstation-qt/autoupdaterdialog.cpp b/src/duckstation-qt/autoupdaterdialog.cpp
index 7dbe62550..f84642674 100644
--- a/src/duckstation-qt/autoupdaterdialog.cpp
+++ b/src/duckstation-qt/autoupdaterdialog.cpp
@@ -334,8 +334,9 @@ void AutoUpdaterDialog::queueUpdateCheck(bool display_errors, bool ignore_skippe
AsyncHTTPRequest* const req = new AsyncHTTPRequest();
connect(req, &AsyncHTTPRequest::requestComplete, this,
- [this, display_errors](s32 status_code, Error& error, std::string& content_type, std::vector& response) {
- getLatestTagComplete(status_code, error, response, display_errors);
+ [this, display_errors](s32 status_code, const std::string& error_message, const std::string& content_type,
+ const std::vector& response) {
+ getLatestTagComplete(status_code, error_message, response, display_errors);
});
req->get(LATEST_TAG_URL, this);
}
@@ -344,14 +345,13 @@ void AutoUpdaterDialog::queueGetLatestRelease()
{
AsyncHTTPRequest* const req = new AsyncHTTPRequest();
connect(req, &AsyncHTTPRequest::requestComplete, this,
- [this](s32 status_code, Error& error, std::string& content_type, std::vector& response) {
- getLatestReleaseComplete(status_code, error, response);
- });
+ [this](s32 status_code, const std::string& error_message, const std::string& content_type,
+ const std::vector& response) { getLatestReleaseComplete(status_code, error_message, response); });
req->get(fmt::format(LATEST_RELEASE_URL, getCurrentUpdateTag()), this);
}
-void AutoUpdaterDialog::getLatestTagComplete(s32 status_code, Error& error, std::vector& response,
- bool display_errors)
+void AutoUpdaterDialog::getLatestTagComplete(s32 status_code, const std::string& error_message,
+ const std::vector& response, bool display_errors)
{
if (handleCancelledRequest(status_code))
return;
@@ -411,13 +411,14 @@ void AutoUpdaterDialog::getLatestTagComplete(s32 status_code, Error& error, std:
else
{
if (display_errors)
- reportError(fmt::format("Failed to download latest tag info: {}", error.GetDescription()));
+ reportError(fmt::format("Failed to download latest tag info: {}", error_message));
}
emit updateCheckCompleted(false);
}
-void AutoUpdaterDialog::getLatestReleaseComplete(s32 status_code, Error& error, std::vector& response)
+void AutoUpdaterDialog::getLatestReleaseComplete(s32 status_code, const std::string& error_message,
+ const std::vector& response)
{
if (handleCancelledRequest(status_code))
return;
@@ -489,7 +490,7 @@ void AutoUpdaterDialog::getLatestReleaseComplete(s32 status_code, Error& error,
}
else
{
- reportError(fmt::format("Failed to download latest release info: {}", error.GetDescription()));
+ reportError(fmt::format("Failed to download latest release info: {}", error_message));
}
emit updateCheckCompleted(false);
@@ -499,15 +500,15 @@ void AutoUpdaterDialog::queueGetChanges()
{
AsyncHTTPRequest* const req = new AsyncHTTPRequest();
connect(req, &AsyncHTTPRequest::requestComplete, this,
- [this](s32 status_code, Error& error, std::string& content_type, std::vector& response) {
- getChangesComplete(status_code, error, response);
- });
+ [this](s32 status_code, const std::string& error_message, const std::string& content_type,
+ const std::vector& response) { getChangesComplete(status_code, error_message, response); });
req->get(fmt::format(CHANGES_URL, g_scm_hash_str, getCurrentUpdateTag()), this);
}
-void AutoUpdaterDialog::getChangesComplete(s32 status_code, Error& error, std::vector& response)
+void AutoUpdaterDialog::getChangesComplete(s32 status_code, const std::string& error_message,
+ const std::vector& response)
{
- std::string_view error_message;
+ std::string_view error_message_to_display;
if (status_code == HTTPDownloader::HTTP_STATUS_OK)
{
@@ -557,19 +558,19 @@ void AutoUpdaterDialog::getChangesComplete(s32 status_code, Error& error, std::v
}
else
{
- error_message = "Change list JSON is not an object";
+ error_message_to_display = "Change list JSON is not an object";
}
}
else
{
- error_message = error.GetDescription();
+ error_message_to_display = error_message;
}
m_ui.updateNotes->setText(QString::fromStdString(
fmt::format("Failed to download change list
The error was:
{}
You may be able to "
"install this update anyway. If the download installation fails, you can download the update "
"from:
" DOWNLOAD_PAGE_URL "
",
- error_message, UPDATER_RELEASE_CHANNEL, UPDATER_RELEASE_CHANNEL)));
+ error_message_to_display, UPDATER_RELEASE_CHANNEL, UPDATER_RELEASE_CHANNEL)));
}
void AutoUpdaterDialog::downloadUpdateClicked()
@@ -587,13 +588,13 @@ void AutoUpdaterDialog::downloadUpdateClicked()
AsyncHTTPRequest* const req = new AsyncHTTPRequest();
connect(req, &AsyncHTTPRequest::requestComplete, this,
- [this](s32 status_code, Error& error, std::string&, std::vector& response) {
- downloadUpdateComplete(status_code, error, response);
- });
+ [this](s32 status_code, const std::string& error_message, const std::string& content_type,
+ const std::vector& response) { downloadUpdateComplete(status_code, error_message, response); });
req->get(m_download_url.toStdString(), this, m_download_progress_callback);
}
-void AutoUpdaterDialog::downloadUpdateComplete(s32 status_code, Error& error, std::vector& response)
+void AutoUpdaterDialog::downloadUpdateComplete(s32 status_code, const std::string& error_message,
+ const std::vector& response)
{
DebugAssert(m_download_progress_callback);
m_download_progress_callback->SetState(TRANSLATE_SV("AutoUpdaterWindow", "Processing Update..."), 1, 1);
@@ -608,7 +609,7 @@ void AutoUpdaterDialog::downloadUpdateComplete(s32 status_code, Error& error, st
if (status_code != HTTPDownloader::HTTP_STATUS_OK)
{
- reportError(fmt::format("Download failed: {}", error.GetDescription()));
+ reportError(fmt::format("Download failed: {}", error_message));
setDownloadSectionVisibility(false);
return;
}
diff --git a/src/duckstation-qt/autoupdaterdialog.h b/src/duckstation-qt/autoupdaterdialog.h
index 785a951a0..b1faa4f14 100644
--- a/src/duckstation-qt/autoupdaterdialog.h
+++ b/src/duckstation-qt/autoupdaterdialog.h
@@ -65,13 +65,14 @@ private:
bool updateNeeded() const;
- void getLatestTagComplete(s32 status_code, Error& error, std::vector& response, bool display_errors);
- void getLatestReleaseComplete(s32 status_code, Error& error, std::vector& response);
+ void getLatestTagComplete(s32 status_code, const std::string& error_message, const std::vector& response,
+ bool display_errors);
+ void getLatestReleaseComplete(s32 status_code, const std::string& error_message, const std::vector& response);
void queueGetChanges();
- void getChangesComplete(s32 status_code, Error& error, std::vector& response);
+ void getChangesComplete(s32 status_code, const std::string& error_message, const std::vector& response);
- void downloadUpdateComplete(s32 status_code, Error& error, std::vector& response);
+ void downloadUpdateComplete(s32 status_code, const std::string& error_message, const std::vector& response);
bool processUpdate(const std::vector& update_data);
#ifdef _WIN32
diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp
index 11124dee3..92ae42e00 100644
--- a/src/duckstation-qt/qthost.cpp
+++ b/src/duckstation-qt/qthost.cpp
@@ -812,10 +812,11 @@ void QtHost::DownloadFile(QWidget* parent, std::string url, std::string path,
bool result = false;
HTTPDownloader::CreateRequest(
std::move(url), parent,
- [&result, &error, &path](s32 status_code, Error& http_error, std::string&, std::vector& hdata) {
+ [&result, &error, &path](s32 status_code, std::string_view http_error, std::string_view content_type,
+ std::vector hdata) {
if (status_code != HTTPDownloader::HTTP_STATUS_OK)
{
- error.SetString(http_error.GetDescription());
+ error.SetStringView(http_error);
return;
}
else if (hdata.empty())
diff --git a/src/util/http_cache.cpp b/src/util/http_cache.cpp
index 4b1e4b1ef..9d3b9ea04 100644
--- a/src/util/http_cache.cpp
+++ b/src/util/http_cache.cpp
@@ -33,8 +33,8 @@ static constexpr u32 CACHE_VERSION = 1;
static void QueueDownload(std::string_view url, FetchCallback callback, Error* error,
std::unique_lock&& lock);
-static void DownloadCallback(const std::string& url, s32 status_code, const Error& error,
- const std::string& content_type, const HTTPDownloader::RequestData& data);
+static void DownloadCallback(const std::string& url, s32 status_code, const std::string_view& error,
+ const HTTPDownloader::RequestData& data);
namespace {
@@ -199,15 +199,14 @@ void HTTPCache::QueueDownload(std::string_view url, FetchCallback callback, Erro
// release lock because CreateRequest() can fire the callback immediately
lock.unlock();
- HTTPDownloader::CreateRequest(std::string(url), &s_locals,
- [url = std::string(url)](s32 status_code, Error& error, std::string& content_type,
- HTTPDownloader::RequestData& data) {
- DownloadCallback(url, status_code, error, content_type, std::move(data));
- });
+ HTTPDownloader::CreateRequest(
+ std::string(url), &s_locals,
+ [url = std::string(url)](s32 status_code, std::string_view error, std::string_view content_type,
+ HTTPDownloader::RequestData data) { DownloadCallback(url, status_code, error, data); });
}
-void HTTPCache::DownloadCallback(const std::string& url, s32 status_code, const Error& error,
- const std::string& content_type, const HTTPDownloader::RequestData& data)
+void HTTPCache::DownloadCallback(const std::string& url, s32 status_code, const std::string_view& error,
+ const HTTPDownloader::RequestData& data)
{
// hold the lock for the insertion, so we don't create a duplicate request as described in Lookup()
std::unique_lock lock(s_locals.pending_downloads_lock);
@@ -228,7 +227,7 @@ void HTTPCache::DownloadCallback(const std::string& url, s32 status_code, const
}
else
{
- ERROR_LOG("Failed to download '{}': HTTP status code {}, error: {}", url, status_code, error.GetDescription());
+ ERROR_LOG("Failed to download '{}': HTTP status code {}, error: {}", url, status_code, error);
}
// invoke all callbacks. uses indexing in case something gets added in the callback
diff --git a/src/util/http_downloader.cpp b/src/util/http_downloader.cpp
index 0b4f19bf3..3235429d3 100644
--- a/src/util/http_downloader.cpp
+++ b/src/util/http_downloader.cpp
@@ -291,7 +291,7 @@ void HTTPDownloader::LockedPollRequests(std::unique_lock& lock
lock.unlock();
req->error.SetStringFmt("Request timed out after {} seconds.", req->timeout_seconds);
- req->callback(HTTP_STATUS_TIMEOUT, req->error, req->content_type, req->data);
+ req->callback(HTTP_STATUS_TIMEOUT, req->error.GetDescription(), {}, {});
CloseRequest(req);
@@ -309,7 +309,7 @@ void HTTPDownloader::LockedPollRequests(std::unique_lock& lock
lock.unlock();
req->error.SetStringView("Request was cancelled.");
- req->callback(HTTP_STATUS_CANCELLED, req->error, req->content_type, req->data);
+ req->callback(HTTP_STATUS_CANCELLED, req->error.GetDescription(), {}, {});
CloseRequest(req);
@@ -347,7 +347,7 @@ void HTTPDownloader::LockedPollRequests(std::unique_lock& lock
else if (req->status_code < 0)
DEV_LOG("Request failed with error {}", req->error.GetDescription());
- req->callback(req->status_code, req->error, req->content_type, req->data);
+ req->callback(req->status_code, req->error.GetDescription(), req->content_type, std::move(req->data));
CloseRequest(req);
lock.lock();
}
@@ -537,8 +537,7 @@ void HTTPDownloader::CancelRequestsForOwner(const void* owner)
s_locals.pending_http_requests.erase(s_locals.pending_http_requests.begin() + index);
lock.unlock();
- req->error.SetStringView("Request was cancelled.");
- req->callback(HTTP_STATUS_CANCELLED, req->error, req->content_type, req->data);
+ req->callback(HTTP_STATUS_CANCELLED, "Request was cancelled.", {}, {});
// If pending, we can delete it immediately since it won't be processed by the worker thread.
// Otherwise, we need to close it so the worker thread can clean up properly.
@@ -557,7 +556,7 @@ void HTTPDownloader::CancelRequestsForOwner(const void* owner)
Host::OnHTTPDownloaderActiveChanged(false);
}
-std::string HTTPDownloader::GetExtensionForContentType(const std::string& content_type)
+std::string_view HTTPDownloader::GetExtensionForContentType(std::string_view content_type)
{
// Based on https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
static constexpr const char* table[][2] = {
@@ -638,16 +637,14 @@ std::string HTTPDownloader::GetExtensionForContentType(const std::string& conten
{"application/x-7z-compressed", "7z"},
};
- std::string ret;
- for (size_t i = 0; i < std::size(table); i++)
+ const std::string_view mime = StringUtil::StripWhitespace(content_type.substr(0, content_type.find(';')));
+ for (const auto& [table_mime, table_extension] : table)
{
- if (StringUtil::Strncasecmp(table[i][0], content_type.data(), content_type.length()) == 0)
- {
- ret = table[i][1];
- break;
- }
+ if (StringUtil::EqualNoCase(mime, table_mime))
+ return table_extension;
}
- return ret;
+
+ return {};
}
#if defined(USE_WINHTTP)
@@ -888,7 +885,7 @@ void HTTPDownloader::InternalCreateRequest(Request::Type type, std::string url,
if (!EnsureInitialized(&req->error))
{
- callback(HTTP_STATUS_ERROR, req->error, req->content_type, req->data);
+ callback(HTTP_STATUS_ERROR, req->error.GetDescription(), req->content_type, req->data);
DeleteRequest(req);
return;
}
@@ -935,7 +932,7 @@ bool HTTPDownloader::StartRequest(Request* req)
const DWORD err = GetLastError();
ERROR_LOG("WinHttpCrackUrl() failed: {}", err);
req->error.SetWin32("WinHttpCrackUrl() failed: ", err);
- req->callback(HTTP_STATUS_ERROR, req->error, req->content_type, req->data);
+ req->callback(HTTP_STATUS_ERROR, req->error.GetDescription(), {}, {});
DeleteRequest(req);
return false;
}
@@ -949,7 +946,7 @@ bool HTTPDownloader::StartRequest(Request* req)
const DWORD err = GetLastError();
ERROR_LOG("Failed to start HTTP request for '{}': {}", req->url, err);
req->error.SetWin32("WinHttpConnect() failed: ", err);
- req->callback(HTTP_STATUS_ERROR, req->error, req->content_type, req->data);
+ req->callback(HTTP_STATUS_ERROR, req->error.GetDescription(), {}, {});
DeleteRequest(req);
return false;
}
@@ -962,7 +959,7 @@ bool HTTPDownloader::StartRequest(Request* req)
const DWORD err = GetLastError();
ERROR_LOG("WinHttpOpenRequest() failed: {}", err);
req->error.SetWin32("WinHttpOpenRequest() failed: ", err);
- req->callback(HTTP_STATUS_ERROR, req->error, req->content_type, req->data);
+ req->callback(HTTP_STATUS_ERROR, req->error.GetDescription(), {}, {});
WinHttpCloseHandle(req->hConnection);
DeleteRequest(req);
return false;
@@ -976,7 +973,7 @@ bool HTTPDownloader::StartRequest(Request* req)
const DWORD err = GetLastError();
ERROR_LOG("WinHttpAddRequestHeaders() failed: {}", err);
req->error.SetWin32("WinHttpAddRequestHeaders() failed: ", err);
- req->callback(HTTP_STATUS_ERROR, req->error, req->content_type, req->data);
+ req->callback(HTTP_STATUS_ERROR, req->error.GetDescription(), {}, {});
WinHttpCloseHandle(req->hRequest);
WinHttpCloseHandle(req->hConnection);
DeleteRequest(req);
@@ -1144,7 +1141,7 @@ void HTTPDownloader::InternalCreateRequest(Request::Type type, std::string url,
if (!EnsureInitialized(&req->error))
{
- callback(HTTP_STATUS_ERROR, req->error, req->content_type, req->data);
+ callback(HTTP_STATUS_ERROR, req->error.GetDescription(), req->content_type, req->data);
DeleteRequest(req);
return;
}
@@ -1290,8 +1287,7 @@ bool HTTPDownloader::StartRequest(Request* req)
if (!req->handle)
{
ERROR_LOG("curl_easy_init() failed");
- req->error.SetStringView("curl_easy_init() failed");
- req->callback(HTTP_STATUS_ERROR, req->error, req->content_type, req->data);
+ req->callback(HTTP_STATUS_ERROR, "curl_easy_init() failed", {}, {});
DeleteRequest(req);
return false;
}
diff --git a/src/util/http_downloader.h b/src/util/http_downloader.h
index 60b65a009..77fd8dd76 100644
--- a/src/util/http_downloader.h
+++ b/src/util/http_downloader.h
@@ -29,12 +29,12 @@ using RequestData = std::vector;
/// Callback fired when a request completes, times out, or is cancelled.
/// Invoked on the thread that calls PollRequests(), with no internal locks held.
///
-/// @param status_code HTTP status code, or one of the negative HTTP_STATUS_* sentinels on failure.
-/// @param error Populated with a description when status_code < HTTP_STATUS_OK.
-/// @param content_type Value of the response Content-Type header; empty if unavailable.
-/// @param data Response body; empty if the request did not succeed.
+/// @param status_code HTTP status code, or one of the negative HTTP_STATUS_* sentinels on failure.
+/// @param error_message Populated with a description when status_code < HTTP_STATUS_OK.
+/// @param content_type Value of the response Content-Type header; empty if unavailable.
+/// @param data Response body; empty if the request did not succeed.
using RequestCallback =
- std::function;
+ std::function;
/// Synthetic status codes used in place of a real HTTP status on failure.
enum : s32
@@ -47,7 +47,7 @@ enum : s32
/// Returns the file extension (without leading dot) for the given MIME type,
/// or an empty string if the type is not recognised.
-std::string GetExtensionForContentType(const std::string& content_type);
+std::string_view GetExtensionForContentType(std::string_view content_type);
/// Sets the default timeout applied to new requests when none is explicitly provided.
/// The initial default is 30 seconds.