HTTPDownloader: Fix user agent sending on Windows/Android

This commit is contained in:
Connor McLaughlin 2021-04-04 12:55:03 +10:00
parent 251043f11a
commit d41b5be908
10 changed files with 40 additions and 33 deletions

View file

@ -19,16 +19,16 @@ AndroidHTTPDownloader::~AndroidHTTPDownloader()
env->DeleteGlobalRef(m_URLDownloader_class); env->DeleteGlobalRef(m_URLDownloader_class);
} }
std::unique_ptr<HTTPDownloader> HTTPDownloader::Create() std::unique_ptr<HTTPDownloader> HTTPDownloader::Create(const char* user_agent)
{ {
std::unique_ptr<AndroidHTTPDownloader> instance(std::make_unique<AndroidHTTPDownloader>()); std::unique_ptr<AndroidHTTPDownloader> instance(std::make_unique<AndroidHTTPDownloader>());
if (!instance->Initialize()) if (!instance->Initialize(user_agent))
return {}; return {};
return instance; return instance;
} }
bool AndroidHTTPDownloader::Initialize() bool AndroidHTTPDownloader::Initialize(const char* user_agent)
{ {
JNIEnv* env = AndroidHelpers::GetJNIEnv(); JNIEnv* env = AndroidHelpers::GetJNIEnv();
jclass klass = env->FindClass("com/github/stenzek/duckstation/URLDownloader"); jclass klass = env->FindClass("com/github/stenzek/duckstation/URLDownloader");
@ -39,7 +39,7 @@ bool AndroidHTTPDownloader::Initialize()
if (!m_URLDownloader_class) if (!m_URLDownloader_class)
return false; return false;
m_URLDownloader_constructor = env->GetMethodID(klass, "<init>", "()V"); m_URLDownloader_constructor = env->GetMethodID(klass, "<init>", "(Ljava/lang/String;)V");
m_URLDownloader_get = env->GetMethodID(klass, "get", "(Ljava/lang/String;)Z"); 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_post = env->GetMethodID(klass, "post", "(Ljava/lang/String;[B)Z");
m_URLDownloader_getStatusCode = env->GetMethodID(klass, "getStatusCode", "()I"); m_URLDownloader_getStatusCode = env->GetMethodID(klass, "getStatusCode", "()I");
@ -50,6 +50,7 @@ bool AndroidHTTPDownloader::Initialize()
return false; return false;
} }
m_user_agent = user_agent;
m_thread_pool = std::make_unique<cb::ThreadPool>(m_max_active_requests); m_thread_pool = std::make_unique<cb::ThreadPool>(m_max_active_requests);
return true; return true;
} }
@ -68,8 +69,10 @@ void AndroidHTTPDownloader::ProcessRequest(Request* req)
JNIEnv* env; JNIEnv* env;
if (AndroidHelpers::GetJavaVM()->AttachCurrentThread(&env, nullptr) == JNI_OK) 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 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; jboolean result;
if (req->post_data.empty()) if (req->post_data.empty())
{ {
@ -85,6 +88,7 @@ void AndroidHTTPDownloader::ProcessRequest(Request* req)
} }
env->DeleteLocalRef(url_string); env->DeleteLocalRef(url_string);
env->DeleteLocalRef(user_agent_string);
if (result) if (result)
{ {

View file

@ -14,7 +14,7 @@ public:
AndroidHTTPDownloader(); AndroidHTTPDownloader();
~AndroidHTTPDownloader() override; ~AndroidHTTPDownloader() override;
bool Initialize(); bool Initialize(const char* user_agent);
protected: protected:
Request* InternalCreateRequest() override; Request* InternalCreateRequest() override;
@ -30,6 +30,7 @@ private:
void ProcessRequest(Request* req); void ProcessRequest(Request* req);
std::string m_user_agent;
std::unique_ptr<cb::ThreadPool> m_thread_pool; std::unique_ptr<cb::ThreadPool> m_thread_pool;
std::mutex m_cancel_mutex; std::mutex m_cancel_mutex;

View file

@ -15,17 +15,21 @@ import java.net.URL;
public class URLDownloader { public class URLDownloader {
private int statusCode = -1; private int statusCode = -1;
private byte[] data = null; 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 { try {
final URL parsedUrl = new URL(url); final URL parsedUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) parsedUrl.openConnection(); HttpURLConnection connection = (HttpURLConnection) parsedUrl.openConnection();
if (connection == null) if (connection == null)
throw new RuntimeException(String.format("openConnection(%s) returned null", url)); throw new RuntimeException(String.format("openConnection(%s) returned null", url));
if (userAgent != null)
connection.addRequestProperty("User-Agent", userAgent);
return connection; return connection;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -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) 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) if (!s_http_downloader)
{ {
Log_ErrorPrint("Failed to create HTTP downloader, cannot use cheevos"); Log_ErrorPrint("Failed to create HTTP downloader, cannot use cheevos");
return false; return false;
} }
s_http_downloader->SetUserAgent(GetUserAgent());
g_active = true; g_active = true;
g_challenge_mode = challenge_mode; g_challenge_mode = challenge_mode;
s_test_mode = test_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 // create a temporary downloader if we're not initialized
Assert(!g_active); Assert(!g_active);
std::unique_ptr<FrontendCommon::HTTPDownloader> http_downloader = FrontendCommon::HTTPDownloader::Create(); std::unique_ptr<FrontendCommon::HTTPDownloader> http_downloader =
FrontendCommon::HTTPDownloader::Create(GetUserAgent().c_str());
if (!http_downloader) if (!http_downloader)
return false; return false;
http_downloader->SetUserAgent(GetUserAgent());
SendLogin(username, password, http_downloader.get(), LoginCallback); SendLogin(username, password, http_downloader.get(), LoginCallback);
http_downloader->WaitForAllRequests(); http_downloader->WaitForAllRequests();

View file

@ -4,26 +4,21 @@
#include "common/timer.h" #include "common/timer.h"
Log_SetChannel(HTTPDownloader); 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 float DEFAULT_TIMEOUT_IN_SECONDS = 30;
static constexpr u32 DEFAULT_MAX_ACTIVE_REQUESTS = 4; static constexpr u32 DEFAULT_MAX_ACTIVE_REQUESTS = 4;
namespace FrontendCommon { 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() HTTPDownloader::HTTPDownloader()
: m_user_agent(DEFAULT_USER_AGENT), m_timeout(DEFAULT_TIMEOUT_IN_SECONDS), : m_timeout(DEFAULT_TIMEOUT_IN_SECONDS), m_max_active_requests(DEFAULT_MAX_ACTIVE_REQUESTS)
m_max_active_requests(DEFAULT_MAX_ACTIVE_REQUESTS)
{ {
} }
HTTPDownloader::~HTTPDownloader() = default; HTTPDownloader::~HTTPDownloader() = default;
void HTTPDownloader::SetUserAgent(std::string name)
{
m_user_agent = std::move(name);
}
void HTTPDownloader::SetTimeout(float timeout) void HTTPDownloader::SetTimeout(float timeout)
{ {
m_timeout = timeout; m_timeout = timeout;

View file

@ -52,9 +52,8 @@ public:
HTTPDownloader(); HTTPDownloader();
virtual ~HTTPDownloader(); virtual ~HTTPDownloader();
static std::unique_ptr<HTTPDownloader> Create(); static std::unique_ptr<HTTPDownloader> Create(const char* user_agent = DEFAULT_USER_AGENT);
void SetUserAgent(std::string name);
void SetTimeout(float timeout); void SetTimeout(float timeout);
void SetMaxActiveRequests(u32 max_active_requests); void SetMaxActiveRequests(u32 max_active_requests);
@ -63,6 +62,8 @@ public:
void PollRequests(); void PollRequests();
void WaitForAllRequests(); void WaitForAllRequests();
static const char DEFAULT_USER_AGENT[];
protected: protected:
virtual Request* InternalCreateRequest() = 0; virtual Request* InternalCreateRequest() = 0;
virtual void InternalPollRequests() = 0; virtual void InternalPollRequests() = 0;

View file

@ -13,10 +13,10 @@ HTTPDownloaderCurl::HTTPDownloaderCurl() : HTTPDownloader() {}
HTTPDownloaderCurl::~HTTPDownloaderCurl() = default; HTTPDownloaderCurl::~HTTPDownloaderCurl() = default;
std::unique_ptr<HTTPDownloader> HTTPDownloader::Create() std::unique_ptr<HTTPDownloader> HTTPDownloader::Create(const char* user_agent)
{ {
std::unique_ptr<HTTPDownloaderCurl> instance(std::make_unique<HTTPDownloaderCurl>()); std::unique_ptr<HTTPDownloaderCurl> instance(std::make_unique<HTTPDownloaderCurl>());
if (!instance->Initialize()) if (!instance->Initialize(user_agent))
return {}; return {};
return instance; return instance;
@ -25,7 +25,7 @@ std::unique_ptr<HTTPDownloader> HTTPDownloader::Create()
static bool s_curl_initialized = false; static bool s_curl_initialized = false;
static std::once_flag s_curl_initialized_once_flag; static std::once_flag s_curl_initialized_once_flag;
bool HTTPDownloaderCurl::Initialize() bool HTTPDownloaderCurl::Initialize(const char* user_agent)
{ {
if (!s_curl_initialized) if (!s_curl_initialized)
{ {
@ -45,6 +45,8 @@ bool HTTPDownloaderCurl::Initialize()
return false; return false;
} }
} }
m_user_agent = user_agent;
m_thread_pool = std::make_unique<cb::ThreadPool>(m_max_active_requests); m_thread_pool = std::make_unique<cb::ThreadPool>(m_max_active_requests);
return true; return true;
} }
@ -75,8 +77,8 @@ void HTTPDownloaderCurl::ProcessRequest(Request* req)
long response_code = 0; long response_code = 0;
curl_easy_getinfo(req->handle, CURLINFO_RESPONSE_CODE, &response_code); curl_easy_getinfo(req->handle, CURLINFO_RESPONSE_CODE, &response_code);
req->status_code = static_cast<s32>(response_code); req->status_code = static_cast<s32>(response_code);
Log_DevPrintf("Request for '%s' returned status code %d and %zu bytes", Log_DevPrintf("Request for '%s' returned status code %d and %zu bytes", req->url.c_str(), req->status_code,
req->url.c_str(), req->status_code, req->data.size()); req->data.size());
} }
else else
{ {

View file

@ -14,7 +14,7 @@ public:
HTTPDownloaderCurl(); HTTPDownloaderCurl();
~HTTPDownloaderCurl() override; ~HTTPDownloaderCurl() override;
bool Initialize(); bool Initialize(const char* user_agent);
protected: protected:
Request* InternalCreateRequest() override; Request* InternalCreateRequest() override;
@ -32,6 +32,7 @@ private:
static size_t WriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata); static size_t WriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata);
void ProcessRequest(Request* req); void ProcessRequest(Request* req);
std::string m_user_agent;
std::unique_ptr<cb::ThreadPool> m_thread_pool; std::unique_ptr<cb::ThreadPool> m_thread_pool;
std::mutex m_cancel_mutex; std::mutex m_cancel_mutex;
}; };

View file

@ -22,16 +22,16 @@ HTTPDownloaderWinHttp::~HTTPDownloaderWinHttp()
} }
} }
std::unique_ptr<HTTPDownloader> HTTPDownloader::Create() std::unique_ptr<HTTPDownloader> HTTPDownloader::Create(const char* user_agent)
{ {
std::unique_ptr<HTTPDownloaderWinHttp> instance(std::make_unique<HTTPDownloaderWinHttp>()); std::unique_ptr<HTTPDownloaderWinHttp> instance(std::make_unique<HTTPDownloaderWinHttp>());
if (!instance->Initialize()) if (!instance->Initialize(user_agent))
return {}; return {};
return instance; return instance;
} }
bool HTTPDownloaderWinHttp::Initialize() bool HTTPDownloaderWinHttp::Initialize(const char* user_agent)
{ {
// WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY is not supported before Win8.1. // WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY is not supported before Win8.1.
const DWORD dwAccessType = const DWORD dwAccessType =

View file

@ -13,7 +13,7 @@ public:
HTTPDownloaderWinHttp(); HTTPDownloaderWinHttp();
~HTTPDownloaderWinHttp() override; ~HTTPDownloaderWinHttp() override;
bool Initialize(); bool Initialize(const char* user_agent);
protected: protected:
Request* InternalCreateRequest() override; Request* InternalCreateRequest() override;