refactor: Implement HTTP client with proxy support (#2146)

This commit is contained in:
Andreas Maerten 2023-10-30 21:18:32 +01:00 committed by GitHub
parent 0de9182735
commit 59244c75bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 435 additions and 28 deletions

View File

@ -19,7 +19,6 @@ AlwaysBreakTemplateDeclarations: 'Yes'
BinPackArguments: 'false'
BinPackParameters: 'true'
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: 'false'
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon

View File

@ -5,7 +5,7 @@ set(CURL_DISABLE_TESTS OFF)
FetchContent_Declare(
cpr
GIT_REPOSITORY https://github.com/libcpr/cpr.git
GIT_TAG 1986262ba4e0cb052161e9e7919aef5ef08217f0
GIT_TAG 1.10.5
GIT_PROGRESS TRUE
)
message("cpr")

View File

@ -0,0 +1,46 @@
#include "http_client.hpp"
namespace big
{
http_client::http_client() :
m_proxy_mgr(m_session)
{
m_session.SetConnectTimeout(CONNECT_TIMEOUT);
m_session.SetTimeout(REQUEST_TIMEOUT);
}
bool http_client::download(const cpr::Url& url, const std::filesystem::path& path, cpr::Header headers, cpr::Parameters query_params)
{
m_session.SetUrl(url);
m_session.SetHeader(headers);
m_session.SetParameters(query_params);
std::ofstream of(path, std::ios::binary);
auto res = m_session.Download(of);
return res.status_code == 200;
}
cpr::Response http_client::get(const cpr::Url& url, cpr::Header headers, cpr::Parameters query_params)
{
m_session.SetUrl(url);
m_session.SetHeader(headers);
m_session.SetParameters(query_params);
return m_session.Get();
}
cpr::Response http_client::post(const cpr::Url& url, cpr::Header headers, cpr::Body body)
{
m_session.SetUrl(url);
m_session.SetHeader(headers);
m_session.SetBody(body);
return m_session.Post();
}
bool http_client::init(file proxy_settings_file)
{
return m_proxy_mgr.load(proxy_settings_file);
}
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <cpr/cpr.h>
#include "proxy_mgr.hpp"
namespace big
{
constexpr auto CONNECT_TIMEOUT = 1000;
constexpr auto REQUEST_TIMEOUT = 5000;
class http_client
{
private:
cpr::Session m_session;
proxy_mgr m_proxy_mgr;
public:
http_client();
virtual ~http_client() = default;
http_client(const http_client&) = delete;
http_client(http_client&&) noexcept = delete;
http_client& operator=(const http_client&) = delete;
http_client& operator=(http_client&&) noexcept = delete;
bool download(const cpr::Url& url, const std::filesystem::path& path, cpr::Header headers = {}, cpr::Parameters query_params = {});
cpr::Response get(const cpr::Url& url, cpr::Header headers = {}, cpr::Parameters query_params = {});
cpr::Response post(const cpr::Url& url, cpr::Header headers = {}, cpr::Body body = {});
proxy_mgr& proxy_mgr()
{
return m_proxy_mgr;
}
bool init(file proxy_settings_file);
};
inline auto g_http_client = http_client();
}

View File

@ -0,0 +1,149 @@
#include "proxy_mgr.hpp"
namespace big
{
proxy_mgr::proxy_mgr(cpr::Session& session) :
m_session(session),
m_protocols({
{ProxyProtocol::NONE, "none"},
{ProxyProtocol::HTTP, "http"},
{ProxyProtocol::HTTPS, "https"},
{ProxyProtocol::SOCKS4, "socks4"},
{ProxyProtocol::SOCKS4A, "socks4a"},
{ProxyProtocol::SOCKS5, "socks5"},
{ProxyProtocol::SOCKS5H, "socks5h"},
})
{
}
bool proxy_mgr::load(file proxy_settings_file)
{
m_proxy_settings_file = proxy_settings_file;
if (proxy_settings_file.exists())
{
auto settings_file = std::ifstream(proxy_settings_file.get_path(), std::ios::binary);
try
{
nlohmann::json j;
settings_file >> j;
m_proxy_settings = j.get<proxy_settings>();
}
catch (const std::exception& e)
{
LOG(WARNING) << "Failed to parse proxy settings file: " << e.what();
return false;
}
}
if (m_proxy_settings.creds.uses_creds)
{
update(m_proxy_settings.proxy_host,
m_proxy_settings.proxy_port,
m_proxy_settings.protocol,
m_proxy_settings.creds.user,
m_proxy_settings.creds.password);
return true;
}
update(m_proxy_settings.proxy_host, m_proxy_settings.proxy_port, m_proxy_settings.protocol);
return true;
}
void proxy_mgr::update(const std::string& host, const int port, const ProxyProtocol protocol)
{
m_proxy_settings.protocol = protocol;
m_proxy_settings.proxy_host = host;
m_proxy_settings.proxy_port = port;
m_proxy_settings.creds = {};
apply_proxy_to_session();
save();
}
void proxy_mgr::update(const std::string& host, const int port, const ProxyProtocol protocol, const std::string& user, const std::string& password)
{
m_proxy_settings.protocol = protocol;
m_proxy_settings.proxy_host = host;
m_proxy_settings.proxy_port = port;
m_proxy_settings.creds.uses_creds = true;
m_proxy_settings.creds.user = user;
m_proxy_settings.creds.password = password;
apply_proxy_to_session();
save();
}
void proxy_mgr::reset()
{
m_proxy_settings = {};
save();
}
std::string proxy_mgr::protocol_str() const
{
return m_protocols.at(m_proxy_settings.protocol);
}
const std::string& proxy_mgr::protocol_str(ProxyProtocol protocol) const
{
return m_protocols.at(protocol);
}
void proxy_mgr::apply_proxy_to_session()
{
if (m_proxy_settings.protocol == ProxyProtocol::NONE)
{
m_session.SetProxies({});
m_session.SetProxyAuth({});
return;
}
std::map<std::string, std::string> proxies;
std::map<std::string, cpr::EncodedAuthentication> proxy_auths;
const auto proxy_auth = cpr::EncodedAuthentication{m_proxy_settings.creds.user, m_proxy_settings.creds.password};
for (const auto& supported_protocol : m_supported_protocols)
{
proxies.insert({supported_protocol, build_url(m_proxy_settings.proxy_host, std::to_string(m_proxy_settings.proxy_port))});
if (m_proxy_settings.creds.uses_creds)
{
proxy_auths.insert({supported_protocol, proxy_auth});
}
}
m_session.SetProxies(proxies);
if (m_proxy_settings.creds.uses_creds)
m_session.SetProxyAuth(proxy_auths);
}
std::string proxy_mgr::build_url(const std::string& host, const std::string& port) const
{
return protocol_str() + "://" + host + ":" + port;
}
bool proxy_mgr::save()
{
try
{
auto settings_file = std::ofstream(m_proxy_settings_file.get_path(), std::ios::binary | std::ios::trunc);
nlohmann::json j = m_proxy_settings;
settings_file << j;
}
catch (const std::exception& e)
{
std::cerr << e.what() << '\n';
return false;
}
return true;
}
}

View File

@ -0,0 +1,42 @@
#pragma once
#include <cpr/cpr.h>
#include "proxy_protocols.hpp"
#include "proxy_settings.hpp"
namespace big
{
class proxy_mgr
{
private:
cpr::Session& m_session;
// protocols supported by CURL
std::unordered_map<ProxyProtocol, std::string> m_protocols;
// protocols to be proxied
std::array<const std::string, 2> m_supported_protocols = { "http", "https" };
proxy_settings m_proxy_settings;
file m_proxy_settings_file;
public:
proxy_mgr(cpr::Session& session);
bool load(file proxy_settings_file);
void update(const std::string& host, const int port, const ProxyProtocol protocol);
void update(const std::string& host, const int port, const ProxyProtocol protocol, const std::string& user, const std::string& password);
void reset();
std::string protocol_str() const;
const std::string& protocol_str(ProxyProtocol protocol) const;
const auto& protocols() const
{ return m_protocols; }
const proxy_settings& settings() const
{ return m_proxy_settings; }
private:
void apply_proxy_to_session();
std::string build_url(const std::string& host, const std::string& port) const;
bool save();
};
}

View File

@ -0,0 +1,34 @@
#pragma once
namespace big
{
enum class ProxyProtocol : uint32_t
{
NONE,
HTTP,
HTTPS,
SOCKS4,
SOCKS4A,
SOCKS5,
SOCKS5H,
FTP,
SMTP,
POP3,
IMAP,
DNS,
FTP_GATEWAY,
TELNET,
SIP,
WEBSOCKETS,
RTSP,
PPTP,
L2TP,
MPLS,
IPSEC,
REVERSE_PROXY,
TRANSPARENT_PROXY,
ANONYMIZING_PROXY,
FORWARD_PROXY,
};
NLOHMANN_JSON_SERIALIZE_ENUM(ProxyProtocol, {{ProxyProtocol::NONE, "none"}, {ProxyProtocol::HTTP, "http"}, {ProxyProtocol::HTTPS, "https"}, {ProxyProtocol::SOCKS4, "socks4"}, {ProxyProtocol::SOCKS5, "socks5"}, {ProxyProtocol::FTP, "ftp"}, {ProxyProtocol::SMTP, "smtp"}, {ProxyProtocol::POP3, "pop3"}, {ProxyProtocol::IMAP, "imap"}, {ProxyProtocol::DNS, "dns"}, {ProxyProtocol::FTP_GATEWAY, "ftp_gateway"}, {ProxyProtocol::TELNET, "telnet"}, {ProxyProtocol::SIP, "sip"}, {ProxyProtocol::WEBSOCKETS, "websockets"}, {ProxyProtocol::RTSP, "rtsp"}, {ProxyProtocol::PPTP, "pptp"}, {ProxyProtocol::L2TP, "l2tp"}, {ProxyProtocol::MPLS, "mpls"}, {ProxyProtocol::IPSEC, "ipsec"}, {ProxyProtocol::REVERSE_PROXY, "reverse_proxy"}, {ProxyProtocol::TRANSPARENT_PROXY, "transparent_proxy"}, {ProxyProtocol::ANONYMIZING_PROXY, "anonymizing_proxy"}, {ProxyProtocol::FORWARD_PROXY, "forward_proxy"}})
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "proxy_protocols.hpp"
namespace big
{
struct proxy_settings
{
std::string proxy_host;
int proxy_port;
ProxyProtocol protocol = ProxyProtocol::NONE;
struct credentials
{
bool uses_creds;
std::string user;
std::string password;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(credentials, uses_creds, user, password)
} creds{};
NLOHMANN_DEFINE_TYPE_INTRUSIVE(proxy_settings, proxy_host, proxy_port, protocol, creds)
};
}

View File

@ -4,6 +4,7 @@
#include "fiber_pool.hpp"
#include "gui.hpp"
#include "hooking.hpp"
#include "http_client/http_client.hpp"
#include "logger/exception_handler.hpp"
#include "lua/lua_manager.hpp"
#include "native_hooks/native_hooks.hpp"
@ -35,6 +36,7 @@
#include "util/migrate.hpp"
#include "version.hpp"
BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
{
using namespace big;
@ -84,6 +86,9 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
auto fiber_pool_instance = std::make_unique<fiber_pool>(11);
LOG(INFO) << "Fiber pool initialized.";
g_http_client.init(g_file_manager.get_project_file("./proxy_settings.json"));
LOG(INFO) << "HTTP Client initialized.";
g_translation_service.init();
LOG(INFO) << "Translation Service initialized.";

View File

@ -1,8 +1,10 @@
#include "api_service.hpp"
#include "http_client/http_client.hpp"
#include "pointers.hpp"
#include "services/creator_storage/creator_storage_service.hpp"
namespace big
{
@ -18,8 +20,7 @@ namespace big
bool api_service::get_rid_from_username(std::string_view username, uint64_t& result)
{
cpr::Response response = cpr::Post(cpr::Url{"https://scui.rockstargames.com/api/friend/accountsearch"}, cpr::Header{{"Authorization", AUTHORIZATION_TICKET}, {"X-Requested-With", "XMLHttpRequest"}}, cpr::Body{{std::format("searchNickname={}", username)}});
const auto response = g_http_client.post("https://scui.rockstargames.com/api/friend/accountsearch", {{"Authorization", AUTHORIZATION_TICKET}, {"X-Requested-With", "XMLHttpRequest"}}, {std::format("searchNickname={}", username)});
if (response.status_code == 200)
{
try
@ -43,8 +44,7 @@ namespace big
bool api_service::get_username_from_rid(uint64_t rid, std::string& result)
{
cpr::Response response = cpr::Post(cpr::Url{"https://scui.rockstargames.com/api/friend/getprofile"}, cpr::Header{{"Authorization", AUTHORIZATION_TICKET}, {"X-Requested-With", "XMLHttpRequest"}, {"Content-Type", "application/json"}}, cpr::Body{{std::format(R"({{"RockstarId":"{}"}})", rid)}});
const auto response = g_http_client.post("https://scui.rockstargames.com/api/friend/getprofile", {{"Authorization", AUTHORIZATION_TICKET}, {"X-Requested-With", "XMLHttpRequest"}, {"Content-Type", "application/json"}}, std::format(R"({{"RockstarId":"{}"}})", rid));
if (response.status_code == 200)
{
try
@ -65,16 +65,16 @@ namespace big
// Ratelimit: 10 per Minute, if exceeded than 5 min cooldown
bool api_service::send_socialclub_message(uint64_t rid, std::string_view message)
{
cpr::Response response = cpr::Post(cpr::Url{"https://scui.rockstargames.com/api/messaging/sendmessage"}, cpr::Header{{"Authorization", AUTHORIZATION_TICKET}, {"X-Requested-With", "XMLHttpRequest"}, {"Content-Type", "application/json"}}, cpr::Body{{std::format(R"({{"env":"prod","title":"gta5","version":11,"recipientRockstarId":"{}","messageText":"{}"}})", rid, message)}});
const auto response = g_http_client.post("https://scui.rockstargames.com/api/messaging/sendmessage", {{"Authorization", AUTHORIZATION_TICKET}, {"X-Requested-With", "XMLHttpRequest"}, {"Content-Type", "application/json"}}, {std::format(R"({{"env":"prod","title":"gta5","version":11,"recipientRockstarId":"{}","messageText":"{}"}})", rid, message)});
return response.status_code == 200;
}
bool api_service::get_job_details(std::string_view content_id, nlohmann::json& result)
{
cpr::Response response = cpr::Get(cpr::Url{"https://scapi.rockstargames.com/ugc/mission/details"},
cpr::Header{{"X-AMC", "true"}, {"X-Requested-With", "XMLHttpRequest"}},
cpr::Parameters{{"title", "gtav"}, {"contentId", content_id.data()}});
const auto response = g_http_client.get("https://scapi.rockstargames.com/ugc/mission/details",
{{"X-AMC", "true"}, {"X-Requested-With", "XMLHttpRequest"}},
{{"title", "gtav"}, {"contentId", content_id.data()}});
if (response.status_code != 200)
return false;
@ -92,18 +92,17 @@ namespace big
bool api_service::download_job_metadata(std::string_view content_id, int f1, int f0, int lang)
{
cpr::Response response = cpr::Get(cpr::Url{std::format("https://prod.cloud.rockstargames.com/ugc/gta5mission/{}/{}_{}_{}.json",
const auto response = g_http_client.get(std::format("https://prod.cloud.rockstargames.com/ugc/gta5mission/{}/{}_{}_{}.json",
content_id,
f1,
f0,
languages.at(lang))});
languages.at(lang)));
if (response.status_code == 200)
{
std::ofstream of = creator_storage_service::create_file(std::string(content_id) + ".json");
cpr::Response r = cpr::Download(of, response.url);
const auto of = creator_storage_service::create_file(std::string(content_id) + ".json");
return true;
return g_http_client.download(response.url, of);
}
return false;

View File

@ -21,9 +21,9 @@ namespace big
return file_paths;
}
std::ofstream creator_storage_service::create_file(std::string file)
std::filesystem::path creator_storage_service::create_file(std::string file)
{
return std::ofstream(check_jobs_folder().get_file(file).get_path());
return check_jobs_folder().get_file(file).get_path();
}
void creator_storage_service::save_file(std::string_view filename)

View File

@ -8,7 +8,7 @@ namespace big
public:
static std::vector<std::string> list_files();
static std::ofstream create_file(std::string name);
static std::filesystem::path create_file(std::string name);
static void load_file(std::string_view file_name);
static void save_file(std::string_view file_name);

View File

@ -53,6 +53,7 @@ namespace big
REACTION_SETTINGS,
PROTECTION_SETTINGS,
TRANSLATION_SETTINGS,
PROXY_SETTINGS,
DEBUG,
PLAYER,
@ -155,6 +156,7 @@ namespace big
{TAB_DECL(HOTKEY_SETTINGS), view::hotkey_settings}},
{TAB_DECL(REACTION_SETTINGS), view::reaction_settings}},
{TAB_DECL(PROTECTION_SETTINGS), view::protection_settings}},
{TAB_DECL(PROXY_SETTINGS), view::proxy_settings}},
{TAB_DECL(DEBUG), nullptr}},
},
},

View File

@ -2,11 +2,10 @@
#include "fiber_pool.hpp"
#include "file_manager.hpp"
#include "http_client/http_client.hpp"
#include "pointers.hpp"
#include "thread_pool.hpp"
#include <cpr/cpr.h>
namespace big
{
translation_service::translation_service() :
@ -190,8 +189,7 @@ namespace big
{
if (auto it = m_remote_index.translations.find(pack_id.data()); it != m_remote_index.translations.end())
{
cpr::Response response = download_file("/" + it->second.file);
const auto response = download_file("/" + it->second.file);
if (response.status_code == 200)
{
try
@ -217,8 +215,7 @@ namespace big
bool translation_service::download_index()
{
cpr::Response response = download_file("/index.json");
const auto response = download_file("/index.json");
if (response.status_code == 200)
{
try
@ -276,11 +273,9 @@ namespace big
cpr::Response translation_service::download_file(const std::string& filename)
{
cpr::Response response = cpr::Get(cpr::Url{m_url + filename});
auto response = g_http_client.get(m_url + filename);
if (response.status_code != 200)
response = cpr::Get(cpr::Url{m_fallback_url + filename});
response = g_http_client.get(m_fallback_url + filename);
return response;
}

View File

@ -0,0 +1,72 @@
#include "http_client/http_client.hpp"
#include "thread_pool.hpp"
#include "views/view.hpp"
namespace big
{
void view::proxy_settings()
{
ImGui::TextWrapped("PROXY_SETTINGS_DESCRIPTION"_T.data());
static auto settings = g_http_client.proxy_mgr().settings();
if (ImGui::BeginCombo("PROXY_SETTINGS_PROTOCOL"_T.data(), g_http_client.proxy_mgr().protocol_str(settings.protocol).data()))
{
for (const auto [key, protocol] : g_http_client.proxy_mgr().protocols())
{
if (ImGui::Selectable(protocol.data(), key == settings.protocol))
{
settings.protocol = key;
}
}
ImGui::EndCombo();
}
if (settings.protocol != ProxyProtocol::NONE)
{
components::input_text_with_hint("PROXY_SETTINGS_HOST"_T, "example.com", settings.proxy_host);
ImGui::InputInt("PROXY_SETTINGS_PORT"_T.data(), &settings.proxy_port);
settings.proxy_port = std::clamp(settings.proxy_port, 0, 0xffff);
ImGui::Checkbox("PROXY_SETTINGS_USES_CREDENTIALS"_T.data(), &settings.creds.uses_creds);
if (settings.creds.uses_creds)
{
components::input_text_with_hint("PROXY_SETTINGS_USER"_T, "user", settings.creds.user);
components::input_text_with_hint("PROXY_SETTINGS_PASSWORD"_T, "passw0rd", settings.creds.password);
}
}
if (components::button("PROXY_SETTINGS_TEST_CURRENT"_T))
{
g_thread_pool->push([] {
const auto response = g_http_client.get("https://github.com/YimMenu/YimMenu/raw/master/metadata.json");
try
{
const auto j = nlohmann::json::parse(response.text);
g_notification_service->push_success("PROXY_SETTINGS"_T.data(), "PROXY_SETTINGS_TEST_CURRENT_SUCCESS"_T.data());
}
catch (const std::exception& e)
{
g_notification_service->push_error("PROXY_SETTINGS"_T.data(), "PROXY_SETTINGS_TEST_CURRENT_FAIL"_T.data());
}
});
}
if (components::button("RESET"_T))
{
settings = {};
g_http_client.proxy_mgr().reset();
}
ImGui::SameLine();
if (components::button("PROXY_SETTINGS_UPDATE"_T))
{
if (settings.protocol == ProxyProtocol::NONE)
settings = {};
if (settings.creds.uses_creds)
g_http_client.proxy_mgr().update(settings.proxy_host, settings.proxy_port, settings.protocol, settings.creds.user, settings.creds.password);
else
g_http_client.proxy_mgr().update(settings.proxy_host, settings.proxy_port, settings.protocol);
g_notification_service->push("PROXY_SETTINGS"_T.data(), "PROXY_SETTINGS_UPDATE_SUCCESS"_T.data());
}
}
}

View File

@ -49,6 +49,7 @@ namespace big
static void spawn_vehicle();
static void pv();
static void persist_car();
static void proxy_settings();
static void xml_vehicles();
static void fun_vehicle();
static void vehicle_control();