From d41b5be9080ccfcc36135b5ab2939ef51e4eb0f1 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 4 Apr 2021 12:55:03 +1000 Subject: [PATCH] HTTPDownloader: Fix user agent sending on Windows/Android --- android/app/src/cpp/android_http_downloader.cpp | 14 +++++++++----- android/app/src/cpp/android_http_downloader.h | 3 ++- .../github/stenzek/duckstation/URLDownloader.java | 8 ++++++-- src/frontend-common/cheevos.cpp | 7 +++---- src/frontend-common/http_downloader.cpp | 13 ++++--------- src/frontend-common/http_downloader.h | 5 +++-- src/frontend-common/http_downloader_curl.cpp | 12 +++++++----- src/frontend-common/http_downloader_curl.h | 3 ++- src/frontend-common/http_downloader_winhttp.cpp | 6 +++--- src/frontend-common/http_downloader_winhttp.h | 2 +- 10 files changed, 40 insertions(+), 33 deletions(-) diff --git a/android/app/src/cpp/android_http_downloader.cpp b/android/app/src/cpp/android_http_downloader.cpp index f3a1b895c..d1f462641 100644 --- a/android/app/src/cpp/android_http_downloader.cpp +++ b/android/app/src/cpp/android_http_downloader.cpp @@ -19,16 +19,16 @@ AndroidHTTPDownloader::~AndroidHTTPDownloader() env->DeleteGlobalRef(m_URLDownloader_class); } -std::unique_ptr HTTPDownloader::Create() +std::unique_ptr HTTPDownloader::Create(const char* user_agent) { std::unique_ptr instance(std::make_unique()); - if (!instance->Initialize()) + if (!instance->Initialize(user_agent)) return {}; return instance; } -bool AndroidHTTPDownloader::Initialize() +bool AndroidHTTPDownloader::Initialize(const char* user_agent) { JNIEnv* env = AndroidHelpers::GetJNIEnv(); jclass klass = env->FindClass("com/github/stenzek/duckstation/URLDownloader"); @@ -39,7 +39,7 @@ bool AndroidHTTPDownloader::Initialize() if (!m_URLDownloader_class) return false; - m_URLDownloader_constructor = env->GetMethodID(klass, "", "()V"); + m_URLDownloader_constructor = env->GetMethodID(klass, "", "(Ljava/lang/String;)V"); m_URLDownloader_get = env->GetMethodID(klass, "get", "(Ljava/lang/String;)Z"); m_URLDownloader_post = env->GetMethodID(klass, "post", "(Ljava/lang/String;[B)Z"); m_URLDownloader_getStatusCode = env->GetMethodID(klass, "getStatusCode", "()I"); @@ -50,6 +50,7 @@ bool AndroidHTTPDownloader::Initialize() return false; } + m_user_agent = user_agent; m_thread_pool = std::make_unique(m_max_active_requests); return true; } @@ -68,8 +69,10 @@ void AndroidHTTPDownloader::ProcessRequest(Request* req) JNIEnv* env; if (AndroidHelpers::GetJavaVM()->AttachCurrentThread(&env, nullptr) == JNI_OK) { - jobject obj = env->NewObject(m_URLDownloader_class, m_URLDownloader_constructor); jstring url_string = env->NewStringUTF(req->url.c_str()); + jstring user_agent_string = env->NewStringUTF(m_user_agent.c_str()); + + jobject obj = env->NewObject(m_URLDownloader_class, m_URLDownloader_constructor, user_agent_string); jboolean result; if (req->post_data.empty()) { @@ -85,6 +88,7 @@ void AndroidHTTPDownloader::ProcessRequest(Request* req) } env->DeleteLocalRef(url_string); + env->DeleteLocalRef(user_agent_string); if (result) { diff --git a/android/app/src/cpp/android_http_downloader.h b/android/app/src/cpp/android_http_downloader.h index 420627cbb..b5774846b 100644 --- a/android/app/src/cpp/android_http_downloader.h +++ b/android/app/src/cpp/android_http_downloader.h @@ -14,7 +14,7 @@ public: AndroidHTTPDownloader(); ~AndroidHTTPDownloader() override; - bool Initialize(); + bool Initialize(const char* user_agent); protected: Request* InternalCreateRequest() override; @@ -30,6 +30,7 @@ private: void ProcessRequest(Request* req); + std::string m_user_agent; std::unique_ptr m_thread_pool; std::mutex m_cancel_mutex; diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/URLDownloader.java b/android/app/src/main/java/com/github/stenzek/duckstation/URLDownloader.java index 3661ece6d..6da425237 100644 --- a/android/app/src/main/java/com/github/stenzek/duckstation/URLDownloader.java +++ b/android/app/src/main/java/com/github/stenzek/duckstation/URLDownloader.java @@ -15,17 +15,21 @@ import java.net.URL; public class URLDownloader { private int statusCode = -1; private byte[] data = null; + private final String userAgent; - public URLDownloader() { + public URLDownloader(String userAgent) { + this.userAgent = userAgent; } - static private HttpURLConnection getConnection(String url) { + private HttpURLConnection getConnection(String url) { try { final URL parsedUrl = new URL(url); HttpURLConnection connection = (HttpURLConnection) parsedUrl.openConnection(); if (connection == null) throw new RuntimeException(String.format("openConnection(%s) returned null", url)); + if (userAgent != null) + connection.addRequestProperty("User-Agent", userAgent); return connection; } catch (Exception e) { e.printStackTrace(); diff --git a/src/frontend-common/cheevos.cpp b/src/frontend-common/cheevos.cpp index 08a648b2e..32a9d4531 100644 --- a/src/frontend-common/cheevos.cpp +++ b/src/frontend-common/cheevos.cpp @@ -213,14 +213,13 @@ static std::string GetUserAgent() bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence, bool challenge_mode) { - s_http_downloader = FrontendCommon::HTTPDownloader::Create(); + s_http_downloader = FrontendCommon::HTTPDownloader::Create(GetUserAgent().c_str()); if (!s_http_downloader) { Log_ErrorPrint("Failed to create HTTP downloader, cannot use cheevos"); return false; } - s_http_downloader->SetUserAgent(GetUserAgent()); g_active = true; g_challenge_mode = challenge_mode; s_test_mode = test_mode; @@ -408,11 +407,11 @@ bool Login(const char* username, const char* password) // create a temporary downloader if we're not initialized Assert(!g_active); - std::unique_ptr http_downloader = FrontendCommon::HTTPDownloader::Create(); + std::unique_ptr http_downloader = + FrontendCommon::HTTPDownloader::Create(GetUserAgent().c_str()); if (!http_downloader) return false; - http_downloader->SetUserAgent(GetUserAgent()); SendLogin(username, password, http_downloader.get(), LoginCallback); http_downloader->WaitForAllRequests(); diff --git a/src/frontend-common/http_downloader.cpp b/src/frontend-common/http_downloader.cpp index 7e5ff5f26..4ca161649 100644 --- a/src/frontend-common/http_downloader.cpp +++ b/src/frontend-common/http_downloader.cpp @@ -4,26 +4,21 @@ #include "common/timer.h" Log_SetChannel(HTTPDownloader); -static constexpr char DEFAULT_USER_AGENT[] = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0"; static constexpr float DEFAULT_TIMEOUT_IN_SECONDS = 30; static constexpr u32 DEFAULT_MAX_ACTIVE_REQUESTS = 4; namespace FrontendCommon { +const char HTTPDownloader::DEFAULT_USER_AGENT[] = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0"; + HTTPDownloader::HTTPDownloader() - : m_user_agent(DEFAULT_USER_AGENT), m_timeout(DEFAULT_TIMEOUT_IN_SECONDS), - m_max_active_requests(DEFAULT_MAX_ACTIVE_REQUESTS) + : m_timeout(DEFAULT_TIMEOUT_IN_SECONDS), m_max_active_requests(DEFAULT_MAX_ACTIVE_REQUESTS) { } HTTPDownloader::~HTTPDownloader() = default; -void HTTPDownloader::SetUserAgent(std::string name) -{ - m_user_agent = std::move(name); -} - void HTTPDownloader::SetTimeout(float timeout) { m_timeout = timeout; diff --git a/src/frontend-common/http_downloader.h b/src/frontend-common/http_downloader.h index 0e2bd8607..192d9d720 100644 --- a/src/frontend-common/http_downloader.h +++ b/src/frontend-common/http_downloader.h @@ -52,9 +52,8 @@ public: HTTPDownloader(); virtual ~HTTPDownloader(); - static std::unique_ptr Create(); + static std::unique_ptr Create(const char* user_agent = DEFAULT_USER_AGENT); - void SetUserAgent(std::string name); void SetTimeout(float timeout); void SetMaxActiveRequests(u32 max_active_requests); @@ -63,6 +62,8 @@ public: void PollRequests(); void WaitForAllRequests(); + static const char DEFAULT_USER_AGENT[]; + protected: virtual Request* InternalCreateRequest() = 0; virtual void InternalPollRequests() = 0; diff --git a/src/frontend-common/http_downloader_curl.cpp b/src/frontend-common/http_downloader_curl.cpp index ac36fb17b..9d8c97efa 100644 --- a/src/frontend-common/http_downloader_curl.cpp +++ b/src/frontend-common/http_downloader_curl.cpp @@ -13,10 +13,10 @@ HTTPDownloaderCurl::HTTPDownloaderCurl() : HTTPDownloader() {} HTTPDownloaderCurl::~HTTPDownloaderCurl() = default; -std::unique_ptr HTTPDownloader::Create() +std::unique_ptr HTTPDownloader::Create(const char* user_agent) { std::unique_ptr instance(std::make_unique()); - if (!instance->Initialize()) + if (!instance->Initialize(user_agent)) return {}; return instance; @@ -25,7 +25,7 @@ std::unique_ptr HTTPDownloader::Create() static bool s_curl_initialized = false; static std::once_flag s_curl_initialized_once_flag; -bool HTTPDownloaderCurl::Initialize() +bool HTTPDownloaderCurl::Initialize(const char* user_agent) { if (!s_curl_initialized) { @@ -45,6 +45,8 @@ bool HTTPDownloaderCurl::Initialize() return false; } } + + m_user_agent = user_agent; m_thread_pool = std::make_unique(m_max_active_requests); return true; } @@ -75,8 +77,8 @@ void HTTPDownloaderCurl::ProcessRequest(Request* req) long response_code = 0; curl_easy_getinfo(req->handle, CURLINFO_RESPONSE_CODE, &response_code); req->status_code = static_cast(response_code); - Log_DevPrintf("Request for '%s' returned status code %d and %zu bytes", - req->url.c_str(), req->status_code, req->data.size()); + Log_DevPrintf("Request for '%s' returned status code %d and %zu bytes", req->url.c_str(), req->status_code, + req->data.size()); } else { diff --git a/src/frontend-common/http_downloader_curl.h b/src/frontend-common/http_downloader_curl.h index f2663270e..3f18ec30b 100644 --- a/src/frontend-common/http_downloader_curl.h +++ b/src/frontend-common/http_downloader_curl.h @@ -14,7 +14,7 @@ public: HTTPDownloaderCurl(); ~HTTPDownloaderCurl() override; - bool Initialize(); + bool Initialize(const char* user_agent); protected: Request* InternalCreateRequest() override; @@ -32,6 +32,7 @@ private: static size_t WriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata); void ProcessRequest(Request* req); + std::string m_user_agent; std::unique_ptr m_thread_pool; std::mutex m_cancel_mutex; }; diff --git a/src/frontend-common/http_downloader_winhttp.cpp b/src/frontend-common/http_downloader_winhttp.cpp index 44356dd7f..9ac001ae0 100644 --- a/src/frontend-common/http_downloader_winhttp.cpp +++ b/src/frontend-common/http_downloader_winhttp.cpp @@ -22,16 +22,16 @@ HTTPDownloaderWinHttp::~HTTPDownloaderWinHttp() } } -std::unique_ptr HTTPDownloader::Create() +std::unique_ptr HTTPDownloader::Create(const char* user_agent) { std::unique_ptr instance(std::make_unique()); - if (!instance->Initialize()) + if (!instance->Initialize(user_agent)) return {}; return instance; } -bool HTTPDownloaderWinHttp::Initialize() +bool HTTPDownloaderWinHttp::Initialize(const char* user_agent) { // WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY is not supported before Win8.1. const DWORD dwAccessType = diff --git a/src/frontend-common/http_downloader_winhttp.h b/src/frontend-common/http_downloader_winhttp.h index 1f5849a13..5168fbfb0 100644 --- a/src/frontend-common/http_downloader_winhttp.h +++ b/src/frontend-common/http_downloader_winhttp.h @@ -13,7 +13,7 @@ public: HTTPDownloaderWinHttp(); ~HTTPDownloaderWinHttp() override; - bool Initialize(); + bool Initialize(const char* user_agent); protected: Request* InternalCreateRequest() override;