mirror of https://github.com/PCSX2/pcsx2.git
HTTPDownloader: Drop Common namespace
Annoying to type...
This commit is contained in:
parent
e9a4d9702c
commit
c557ea1b6f
|
@ -21,8 +21,6 @@
|
|||
#include "common/StringUtil.h"
|
||||
#include "common/Timer.h"
|
||||
|
||||
using namespace Common;
|
||||
|
||||
static constexpr float DEFAULT_TIMEOUT_IN_SECONDS = 30;
|
||||
static constexpr u32 DEFAULT_MAX_ACTIVE_REQUESTS = 4;
|
||||
|
||||
|
@ -55,7 +53,7 @@ void HTTPDownloader::CreateRequest(std::string url, Request::Callback callback)
|
|||
req->type = Request::Type::Get;
|
||||
req->url = std::move(url);
|
||||
req->callback = std::move(callback);
|
||||
req->start_time = Timer::GetCurrentValue();
|
||||
req->start_time = Common::Timer::GetCurrentValue();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_pending_http_request_lock);
|
||||
if (LockedGetActiveRequestCount() < m_max_active_requests)
|
||||
|
@ -75,7 +73,7 @@ void HTTPDownloader::CreatePostRequest(std::string url, std::string post_data, R
|
|||
req->url = std::move(url);
|
||||
req->post_data = std::move(post_data);
|
||||
req->callback = std::move(callback);
|
||||
req->start_time = Timer::GetCurrentValue();
|
||||
req->start_time = Common::Timer::GetCurrentValue();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_pending_http_request_lock);
|
||||
if (LockedGetActiveRequestCount() < m_max_active_requests)
|
||||
|
@ -94,7 +92,7 @@ void HTTPDownloader::LockedPollRequests(std::unique_lock<std::mutex>& lock)
|
|||
|
||||
InternalPollRequests();
|
||||
|
||||
const Common::Timer::Value current_time = Timer::GetCurrentValue();
|
||||
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
|
||||
u32 active_requests = 0;
|
||||
u32 unstarted_requests = 0;
|
||||
|
||||
|
|
|
@ -23,87 +23,83 @@
|
|||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Common
|
||||
class HTTPDownloader
|
||||
{
|
||||
class HTTPDownloader
|
||||
public:
|
||||
enum : s32
|
||||
{
|
||||
public:
|
||||
enum : s32
|
||||
{
|
||||
HTTP_STATUS_CANCELLED = -3,
|
||||
HTTP_STATUS_TIMEOUT = -2,
|
||||
HTTP_STATUS_ERROR = -1,
|
||||
HTTP_STATUS_OK = 200
|
||||
};
|
||||
|
||||
struct Request
|
||||
{
|
||||
using Data = std::vector<u8>;
|
||||
using Callback = std::function<void(s32 status_code, const std::string& content_type, Data data)>;
|
||||
|
||||
enum class Type
|
||||
{
|
||||
Get,
|
||||
Post,
|
||||
};
|
||||
|
||||
enum class State
|
||||
{
|
||||
Pending,
|
||||
Cancelled,
|
||||
Started,
|
||||
Receiving,
|
||||
Complete,
|
||||
};
|
||||
|
||||
HTTPDownloader* parent;
|
||||
Callback callback;
|
||||
std::string url;
|
||||
std::string post_data;
|
||||
std::string content_type;
|
||||
Data data;
|
||||
u64 start_time;
|
||||
s32 status_code = 0;
|
||||
u32 content_length = 0;
|
||||
Type type = Type::Get;
|
||||
std::atomic<State> state{State::Pending};
|
||||
};
|
||||
|
||||
HTTPDownloader();
|
||||
virtual ~HTTPDownloader();
|
||||
|
||||
static std::unique_ptr<HTTPDownloader> Create(const char* user_agent = DEFAULT_USER_AGENT);
|
||||
static std::string URLEncode(const std::string_view& str);
|
||||
static std::string URLDecode(const std::string_view& str);
|
||||
static std::string GetExtensionForContentType(const std::string& content_type);
|
||||
|
||||
void SetTimeout(float timeout);
|
||||
void SetMaxActiveRequests(u32 max_active_requests);
|
||||
|
||||
void CreateRequest(std::string url, Request::Callback callback);
|
||||
void CreatePostRequest(std::string url, std::string post_data, Request::Callback callback);
|
||||
void PollRequests();
|
||||
void WaitForAllRequests();
|
||||
bool HasAnyRequests();
|
||||
|
||||
static const char DEFAULT_USER_AGENT[];
|
||||
|
||||
protected:
|
||||
virtual Request* InternalCreateRequest() = 0;
|
||||
virtual void InternalPollRequests() = 0;
|
||||
|
||||
virtual bool StartRequest(Request* request) = 0;
|
||||
virtual void CloseRequest(Request* request) = 0;
|
||||
|
||||
void LockedAddRequest(Request* request);
|
||||
u32 LockedGetActiveRequestCount();
|
||||
void LockedPollRequests(std::unique_lock<std::mutex>& lock);
|
||||
|
||||
float m_timeout;
|
||||
u32 m_max_active_requests;
|
||||
|
||||
std::mutex m_pending_http_request_lock;
|
||||
std::vector<Request*> m_pending_http_requests;
|
||||
HTTP_STATUS_CANCELLED = -3,
|
||||
HTTP_STATUS_TIMEOUT = -2,
|
||||
HTTP_STATUS_ERROR = -1,
|
||||
HTTP_STATUS_OK = 200
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
struct Request
|
||||
{
|
||||
using Data = std::vector<u8>;
|
||||
using Callback = std::function<void(s32 status_code, const std::string& content_type, Data data)>;
|
||||
|
||||
enum class Type
|
||||
{
|
||||
Get,
|
||||
Post,
|
||||
};
|
||||
|
||||
enum class State
|
||||
{
|
||||
Pending,
|
||||
Cancelled,
|
||||
Started,
|
||||
Receiving,
|
||||
Complete,
|
||||
};
|
||||
|
||||
HTTPDownloader* parent;
|
||||
Callback callback;
|
||||
std::string url;
|
||||
std::string post_data;
|
||||
std::string content_type;
|
||||
Data data;
|
||||
u64 start_time;
|
||||
s32 status_code = 0;
|
||||
u32 content_length = 0;
|
||||
Type type = Type::Get;
|
||||
std::atomic<State> state{State::Pending};
|
||||
};
|
||||
|
||||
HTTPDownloader();
|
||||
virtual ~HTTPDownloader();
|
||||
|
||||
static std::unique_ptr<HTTPDownloader> Create(const char* user_agent = DEFAULT_USER_AGENT);
|
||||
static std::string URLEncode(const std::string_view& str);
|
||||
static std::string URLDecode(const std::string_view& str);
|
||||
static std::string GetExtensionForContentType(const std::string& content_type);
|
||||
|
||||
void SetTimeout(float timeout);
|
||||
void SetMaxActiveRequests(u32 max_active_requests);
|
||||
|
||||
void CreateRequest(std::string url, Request::Callback callback);
|
||||
void CreatePostRequest(std::string url, std::string post_data, Request::Callback callback);
|
||||
void PollRequests();
|
||||
void WaitForAllRequests();
|
||||
bool HasAnyRequests();
|
||||
|
||||
static const char DEFAULT_USER_AGENT[];
|
||||
|
||||
protected:
|
||||
virtual Request* InternalCreateRequest() = 0;
|
||||
virtual void InternalPollRequests() = 0;
|
||||
|
||||
virtual bool StartRequest(Request* request) = 0;
|
||||
virtual void CloseRequest(Request* request) = 0;
|
||||
|
||||
void LockedAddRequest(Request* request);
|
||||
u32 LockedGetActiveRequestCount();
|
||||
void LockedPollRequests(std::unique_lock<std::mutex>& lock);
|
||||
|
||||
float m_timeout;
|
||||
u32 m_max_active_requests;
|
||||
|
||||
std::mutex m_pending_http_request_lock;
|
||||
std::vector<Request*> m_pending_http_requests;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
|
@ -26,8 +26,6 @@
|
|||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
using namespace Common;
|
||||
|
||||
HTTPDownloaderCurl::HTTPDownloaderCurl()
|
||||
: HTTPDownloader()
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
|
@ -21,34 +21,31 @@
|
|||
#include <mutex>
|
||||
#include <curl/curl.h>
|
||||
|
||||
namespace Common
|
||||
class HTTPDownloaderCurl final : public HTTPDownloader
|
||||
{
|
||||
class HTTPDownloaderCurl final : public HTTPDownloader
|
||||
public:
|
||||
HTTPDownloaderCurl();
|
||||
~HTTPDownloaderCurl() override;
|
||||
|
||||
bool Initialize(const char* user_agent);
|
||||
|
||||
protected:
|
||||
Request* InternalCreateRequest() override;
|
||||
void InternalPollRequests() override;
|
||||
bool StartRequest(HTTPDownloader::Request* request) override;
|
||||
void CloseRequest(HTTPDownloader::Request* request) override;
|
||||
|
||||
private:
|
||||
struct Request : HTTPDownloader::Request
|
||||
{
|
||||
public:
|
||||
HTTPDownloaderCurl();
|
||||
~HTTPDownloaderCurl() override;
|
||||
|
||||
bool Initialize(const char* user_agent);
|
||||
|
||||
protected:
|
||||
Request* InternalCreateRequest() override;
|
||||
void InternalPollRequests() override;
|
||||
bool StartRequest(HTTPDownloader::Request* request) override;
|
||||
void CloseRequest(HTTPDownloader::Request* request) override;
|
||||
|
||||
private:
|
||||
struct Request : HTTPDownloader::Request
|
||||
{
|
||||
CURL* handle = nullptr;
|
||||
std::atomic_bool closed{false};
|
||||
};
|
||||
|
||||
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<cb::ThreadPool> m_thread_pool;
|
||||
std::mutex m_cancel_mutex;
|
||||
CURL* handle = nullptr;
|
||||
std::atomic_bool closed{false};
|
||||
};
|
||||
} // namespace Common
|
||||
|
||||
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<cb::ThreadPool> m_thread_pool;
|
||||
std::mutex m_cancel_mutex;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
|
@ -25,8 +25,6 @@
|
|||
|
||||
#pragma comment(lib, "winhttp.lib")
|
||||
|
||||
using namespace Common;
|
||||
|
||||
HTTPDownloaderWinHttp::HTTPDownloaderWinHttp()
|
||||
: HTTPDownloader()
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
|
@ -20,34 +20,31 @@
|
|||
|
||||
#include <winhttp.h>
|
||||
|
||||
namespace Common
|
||||
class HTTPDownloaderWinHttp final : public HTTPDownloader
|
||||
{
|
||||
class HTTPDownloaderWinHttp final : public HTTPDownloader
|
||||
public:
|
||||
HTTPDownloaderWinHttp();
|
||||
~HTTPDownloaderWinHttp() override;
|
||||
|
||||
bool Initialize(const char* user_agent);
|
||||
|
||||
protected:
|
||||
Request* InternalCreateRequest() override;
|
||||
void InternalPollRequests() override;
|
||||
bool StartRequest(HTTPDownloader::Request* request) override;
|
||||
void CloseRequest(HTTPDownloader::Request* request) override;
|
||||
|
||||
private:
|
||||
struct Request : HTTPDownloader::Request
|
||||
{
|
||||
public:
|
||||
HTTPDownloaderWinHttp();
|
||||
~HTTPDownloaderWinHttp() override;
|
||||
|
||||
bool Initialize(const char* user_agent);
|
||||
|
||||
protected:
|
||||
Request* InternalCreateRequest() override;
|
||||
void InternalPollRequests() override;
|
||||
bool StartRequest(HTTPDownloader::Request* request) override;
|
||||
void CloseRequest(HTTPDownloader::Request* request) override;
|
||||
|
||||
private:
|
||||
struct Request : HTTPDownloader::Request
|
||||
{
|
||||
std::wstring object_name;
|
||||
HINTERNET hConnection = NULL;
|
||||
HINTERNET hRequest = NULL;
|
||||
u32 io_position = 0;
|
||||
};
|
||||
|
||||
static void CALLBACK HTTPStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
|
||||
LPVOID lpvStatusInformation, DWORD dwStatusInformationLength);
|
||||
|
||||
HINTERNET m_hSession = NULL;
|
||||
std::wstring object_name;
|
||||
HINTERNET hConnection = NULL;
|
||||
HINTERNET hRequest = NULL;
|
||||
u32 io_position = 0;
|
||||
};
|
||||
} // namespace Common
|
||||
|
||||
static void CALLBACK HTTPStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
|
||||
LPVOID lpvStatusInformation, DWORD dwStatusInformationLength);
|
||||
|
||||
HINTERNET m_hSession = NULL;
|
||||
};
|
||||
|
|
|
@ -143,8 +143,8 @@ namespace Achievements
|
|||
static void UpdateGameSummary();
|
||||
static void DownloadImage(std::string url, std::string cache_filename);
|
||||
|
||||
static bool CreateClient(rc_client_t** client, std::unique_ptr<Common::HTTPDownloader>* http);
|
||||
static void DestroyClient(rc_client_t** client, std::unique_ptr<Common::HTTPDownloader>* http);
|
||||
static bool CreateClient(rc_client_t** client, std::unique_ptr<HTTPDownloader>* http);
|
||||
static void DestroyClient(rc_client_t** client, std::unique_ptr<HTTPDownloader>* http);
|
||||
static void ClientMessageCallback(const char* message, const rc_client_t* client);
|
||||
static uint32_t ClientReadMemory(uint32_t address, uint8_t* buffer, uint32_t num_bytes, rc_client_t* client);
|
||||
static void ClientServerCall(
|
||||
|
@ -203,7 +203,7 @@ namespace Achievements
|
|||
static std::recursive_mutex s_achievements_mutex;
|
||||
static rc_client_t* s_client;
|
||||
static std::string s_image_directory;
|
||||
static std::unique_ptr<Common::HTTPDownloader> s_http_downloader;
|
||||
static std::unique_ptr<HTTPDownloader> s_http_downloader;
|
||||
|
||||
static u32 s_game_disc_crc;
|
||||
static std::string s_game_hash;
|
||||
|
@ -352,8 +352,8 @@ std::string Achievements::GetGameHash()
|
|||
|
||||
void Achievements::DownloadImage(std::string url, std::string cache_filename)
|
||||
{
|
||||
auto callback = [cache_filename](s32 status_code, std::string content_type, Common::HTTPDownloader::Request::Data data) {
|
||||
if (status_code != Common::HTTPDownloader::HTTP_STATUS_OK)
|
||||
auto callback = [cache_filename](s32 status_code, std::string content_type, HTTPDownloader::Request::Data data) {
|
||||
if (status_code != HTTPDownloader::HTTP_STATUS_OK)
|
||||
return;
|
||||
|
||||
if (!FileSystem::WriteBinaryFile(cache_filename.c_str(), data.data(), data.size()))
|
||||
|
@ -472,9 +472,9 @@ bool Achievements::Initialize()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Achievements::CreateClient(rc_client_t** client, std::unique_ptr<Common::HTTPDownloader>* http)
|
||||
bool Achievements::CreateClient(rc_client_t** client, std::unique_ptr<HTTPDownloader>* http)
|
||||
{
|
||||
*http = Common::HTTPDownloader::Create(GetUserAgent().c_str());
|
||||
*http = HTTPDownloader::Create(GetUserAgent().c_str());
|
||||
if (!*http)
|
||||
{
|
||||
Host::ReportErrorAsync("Achievements Error", "Failed to create HTTPDownloader, cannot use achievements");
|
||||
|
@ -501,7 +501,7 @@ bool Achievements::CreateClient(rc_client_t** client, std::unique_ptr<Common::HT
|
|||
return true;
|
||||
}
|
||||
|
||||
void Achievements::DestroyClient(rc_client_t** client, std::unique_ptr<Common::HTTPDownloader>* http)
|
||||
void Achievements::DestroyClient(rc_client_t** client, std::unique_ptr<HTTPDownloader>* http)
|
||||
{
|
||||
(*http)->WaitForAllRequests();
|
||||
|
||||
|
@ -649,10 +649,10 @@ 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)
|
||||
{
|
||||
Common::HTTPDownloader::Request::Callback hd_callback = [callback, callback_data](s32 status_code, std::string content_type,
|
||||
Common::HTTPDownloader::Request::Data data) {
|
||||
HTTPDownloader::Request::Callback hd_callback = [callback, callback_data](s32 status_code, std::string content_type,
|
||||
HTTPDownloader::Request::Data data) {
|
||||
rc_api_server_response_t rr;
|
||||
rr.http_status_code = (status_code <= 0) ? (status_code == Common::HTTPDownloader::HTTP_STATUS_CANCELLED ?
|
||||
rr.http_status_code = (status_code <= 0) ? (status_code == HTTPDownloader::HTTP_STATUS_CANCELLED ?
|
||||
RC_API_SERVER_RESPONSE_CLIENT_ERROR :
|
||||
RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR) :
|
||||
status_code;
|
||||
|
@ -662,7 +662,7 @@ void Achievements::ClientServerCall(
|
|||
callback(&rr, callback_data);
|
||||
};
|
||||
|
||||
Common::HTTPDownloader* http = static_cast<Common::HTTPDownloader*>(rc_client_get_userdata(client));
|
||||
HTTPDownloader* http = static_cast<HTTPDownloader*>(rc_client_get_userdata(client));
|
||||
|
||||
// TODO: Content-type for post
|
||||
if (request->post_data)
|
||||
|
@ -1645,9 +1645,9 @@ bool Achievements::Login(const char* username, const char* password, Error* erro
|
|||
|
||||
// We need to use a temporary client if achievements aren't currently active.
|
||||
rc_client_t* client = s_client;
|
||||
Common::HTTPDownloader* http = s_http_downloader.get();
|
||||
HTTPDownloader* http = s_http_downloader.get();
|
||||
const bool is_temporary_client = (client == nullptr);
|
||||
std::unique_ptr<Common::HTTPDownloader> temporary_downloader;
|
||||
std::unique_ptr<HTTPDownloader> temporary_downloader;
|
||||
ScopedGuard temporary_client_guard = [&client, is_temporary_client, &temporary_downloader]() {
|
||||
if (is_temporary_client)
|
||||
DestroyClient(&client, &temporary_downloader);
|
||||
|
|
|
@ -1267,14 +1267,14 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
|
|||
{
|
||||
std::string url(url_template);
|
||||
if (has_title)
|
||||
StringUtil::ReplaceAll(&url, "${title}", Common::HTTPDownloader::URLEncode(entry.title));
|
||||
StringUtil::ReplaceAll(&url, "${title}", HTTPDownloader::URLEncode(entry.title));
|
||||
if (has_file_title)
|
||||
{
|
||||
std::string display_name(FileSystem::GetDisplayNameFromPath(entry.path));
|
||||
StringUtil::ReplaceAll(&url, "${filetitle}", Common::HTTPDownloader::URLEncode(Path::GetFileTitle(display_name)));
|
||||
StringUtil::ReplaceAll(&url, "${filetitle}", HTTPDownloader::URLEncode(Path::GetFileTitle(display_name)));
|
||||
}
|
||||
if (has_serial)
|
||||
StringUtil::ReplaceAll(&url, "${serial}", Common::HTTPDownloader::URLEncode(entry.serial));
|
||||
StringUtil::ReplaceAll(&url, "${serial}", HTTPDownloader::URLEncode(entry.serial));
|
||||
|
||||
download_urls.emplace_back(entry.path, std::move(url));
|
||||
}
|
||||
|
@ -1286,7 +1286,7 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
|
|||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Common::HTTPDownloader> downloader(Common::HTTPDownloader::Create());
|
||||
std::unique_ptr<HTTPDownloader> downloader(HTTPDownloader::Create());
|
||||
if (!downloader)
|
||||
{
|
||||
progress->DisplayError("Failed to create HTTP downloader.");
|
||||
|
@ -1315,11 +1315,11 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
|
|||
}
|
||||
|
||||
// we could actually do a few in parallel here...
|
||||
std::string filename(Common::HTTPDownloader::URLDecode(url));
|
||||
std::string filename(HTTPDownloader::URLDecode(url));
|
||||
downloader->CreateRequest(
|
||||
std::move(url), [use_serial, &save_callback, entry_path = std::move(entry_path), filename = std::move(filename)](
|
||||
s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data) {
|
||||
if (status_code != Common::HTTPDownloader::HTTP_STATUS_OK || data.empty())
|
||||
s32 status_code, const std::string& content_type, HTTPDownloader::Request::Data data) {
|
||||
if (status_code != HTTPDownloader::HTTP_STATUS_OK || data.empty())
|
||||
return;
|
||||
|
||||
std::unique_lock lock(s_mutex);
|
||||
|
@ -1330,7 +1330,7 @@ bool GameList::DownloadCovers(const std::vector<std::string>& 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.
|
||||
std::string template_filename;
|
||||
std::string content_type_extension(Common::HTTPDownloader::GetExtensionForContentType(content_type));
|
||||
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('/');
|
||||
|
|
Loading…
Reference in New Issue