Improved error handling of gta_data_service. (#412)
* Bug fix for #403 * Fixed Issue #403 * Minor fixes. * Improved error handling of gta_data_service. Fixed an issue where the game crashes when throwing invalid json format exception. * Minor fixes. * Minor fixes. * Improved error handling of gta_data_service. Also extended the timeout to 30s. * Improved error handling of gta_data_service. - Fixed an issue where http_request doesn't handle HEAD requests and socket.recv error correctly. - Limited gta_data_service to have only 1 active http request. * Minor fixes. * Minor fixes.
This commit is contained in:
parent
454413be30
commit
d6fce67cbc
@ -598,8 +598,16 @@ namespace http
|
|||||||
{
|
{
|
||||||
const auto size = socket.recv(tempBuffer.data(), tempBuffer.size(),
|
const auto size = socket.recv(tempBuffer.data(), tempBuffer.size(),
|
||||||
(timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);
|
(timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);
|
||||||
if (size == 0) // disconnected
|
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
// download complete
|
||||||
return response;
|
return response;
|
||||||
|
}
|
||||||
|
else if (size < 0)
|
||||||
|
{
|
||||||
|
throw ResponseError("Socket error");
|
||||||
|
}
|
||||||
|
|
||||||
responseData.insert(responseData.end(), tempBuffer.begin(), tempBuffer.begin() + size);
|
responseData.insert(responseData.end(), tempBuffer.begin(), tempBuffer.begin() + size);
|
||||||
|
|
||||||
@ -683,8 +691,11 @@ namespace http
|
|||||||
|
|
||||||
if (state == State::body)
|
if (state == State::body)
|
||||||
{
|
{
|
||||||
// Content-Length must be ignored if Transfer-Encoding is received
|
if (method == "HEAD") // Body must be ignored for HEAD requests
|
||||||
if (chunkedResponse)
|
{
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
else if (chunkedResponse) // Content-Length must be ignored if Transfer-Encoding is received
|
||||||
{
|
{
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
@ -22,42 +22,47 @@ namespace big::remote
|
|||||||
|
|
||||||
inline bool update_binary(const std::string_view file_url, const std::filesystem::path& file_location, const std::filesystem::path& etag_location)
|
inline bool update_binary(const std::string_view file_url, const std::filesystem::path& file_location, const std::filesystem::path& etag_location)
|
||||||
{
|
{
|
||||||
try
|
std::string local_etag = "";
|
||||||
{
|
std::string remote_etag = "";
|
||||||
std::string local_etag = "";
|
|
||||||
std::string remote_etag = "";
|
|
||||||
|
|
||||||
const std::vector<std::string> headers = { "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/6.0" };
|
try {
|
||||||
|
|
||||||
try {
|
{
|
||||||
|
std::ifstream file_etag_ifstream(etag_location, std::ios::binary);
|
||||||
|
std::stringstream file_etag_stringstream;
|
||||||
|
file_etag_stringstream << file_etag_ifstream.rdbuf();
|
||||||
|
local_etag = file_etag_stringstream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!local_etag.empty())
|
||||||
|
{
|
||||||
|
http::Request req(file_url.data());
|
||||||
|
http::Response res = req.send("HEAD", "", {}, 20s);
|
||||||
|
|
||||||
|
if (res.status == http::Response::Status::Ok)
|
||||||
{
|
{
|
||||||
std::ifstream file_etag_ifstream(etag_location, std::ios::binary);
|
|
||||||
std::stringstream file_etag_stringstream;
|
|
||||||
file_etag_stringstream << file_etag_ifstream.rdbuf();
|
|
||||||
local_etag = file_etag_stringstream.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!local_etag.empty())
|
|
||||||
{
|
|
||||||
http::Request req(file_url.data());
|
|
||||||
http::Response res = req.send("HEAD", "", headers, 15s);
|
|
||||||
|
|
||||||
remote_etag = get_etag_from_headers(res.headers);
|
remote_etag = get_etag_from_headers(res.headers);
|
||||||
|
|
||||||
if (remote_etag == local_etag)
|
if (remote_etag == local_etag)
|
||||||
{
|
{
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::exception(std::to_string(res.status).c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
}
|
||||||
{
|
catch (const std::exception& e)
|
||||||
LOG(INFO) << "Update Error: " << e.what();
|
{
|
||||||
}
|
LOG(WARNING) << "Update Error (HEAD): " << e.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
http::Request req(file_url.data());
|
http::Request req(file_url.data());
|
||||||
http::Response res = req.send("GET", "", headers, 30s);
|
http::Response res = req.send("GET", "", {}, 20s);
|
||||||
|
|
||||||
if (res.status == http::Response::Status::Ok)
|
if (res.status == http::Response::Status::Ok)
|
||||||
{
|
{
|
||||||
@ -72,12 +77,14 @@ namespace big::remote
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
return false;
|
{
|
||||||
|
throw std::exception(std::to_string(res.status).c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
LOG(INFO) << "Failed to download binary, is the host down?: " << e.what();
|
LOG(WARNING) << "Update Error (GET): " << e.what();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -9,31 +9,40 @@ namespace big
|
|||||||
{
|
{
|
||||||
gta_data_service::gta_data_service()
|
gta_data_service::gta_data_service()
|
||||||
{
|
{
|
||||||
const std::string url_prefix = "http://github-proxy.damon.sh/DurtyFree/gta-v-data-dumps/master/";
|
g_thread_pool->push([this] {
|
||||||
|
while (!g_running)
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
|
|
||||||
load_from_file(
|
const std::string url_prefix = "http://github-proxy.damon.sh/DurtyFree/gta-v-data-dumps/master/";
|
||||||
"./lib/vehicles.json",
|
|
||||||
"./lib/vehicles_etag.txt",
|
|
||||||
url_prefix + "vehicles.json",
|
|
||||||
>a_data_service::load_vehicles,
|
|
||||||
"Vehicle"
|
|
||||||
);
|
|
||||||
|
|
||||||
load_from_file(
|
this->load_from_file(
|
||||||
"./lib/peds.json",
|
"./lib/vehicles.json",
|
||||||
"./lib/peds_etag.txt",
|
"./lib/vehicles_etag.txt",
|
||||||
url_prefix + "peds.json",
|
url_prefix + "vehicles.json",
|
||||||
>a_data_service::load_peds,
|
>a_data_service::load_vehicles,
|
||||||
"Ped"
|
"Vehicle"
|
||||||
);
|
);
|
||||||
|
|
||||||
load_from_file(
|
std::this_thread::sleep_for(1s);
|
||||||
"./lib/weapons.json",
|
|
||||||
"./lib/weapons_etag.txt",
|
this->load_from_file(
|
||||||
url_prefix + "weapons.json",
|
"./lib/peds.json",
|
||||||
>a_data_service::load_weapons,
|
"./lib/peds_etag.txt",
|
||||||
"Weapon"
|
url_prefix + "peds.json",
|
||||||
);
|
>a_data_service::load_peds,
|
||||||
|
"Ped"
|
||||||
|
);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
|
|
||||||
|
this->load_from_file(
|
||||||
|
"./lib/weapons.json",
|
||||||
|
"./lib/weapons_etag.txt",
|
||||||
|
url_prefix + "weapons.json",
|
||||||
|
>a_data_service::load_weapons,
|
||||||
|
"Weapon"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
g_gta_data_service = this;
|
g_gta_data_service = this;
|
||||||
}
|
}
|
||||||
@ -127,64 +136,85 @@ namespace big
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void gta_data_service::load_from_file(std::string file_path, std::string etag_path, std::string url, bool(gta_data_service::* load_func)(file), std::string data_name)
|
void gta_data_service::load_from_file(
|
||||||
{
|
std::string file_path, std::string etag_path, std::string url,
|
||||||
|
bool(gta_data_service::* load_func)(std::filesystem::path), std::string data_name
|
||||||
|
) {
|
||||||
file file_to_load(g_file_manager->get_project_file(file_path));
|
file file_to_load(g_file_manager->get_project_file(file_path));
|
||||||
|
file file_etag(g_file_manager->get_project_file(etag_path));
|
||||||
|
auto file_to_load_path = file_to_load.get_path();
|
||||||
|
auto file_etag_path = file_etag.get_path();
|
||||||
|
|
||||||
if (file_to_load.exists())
|
bool up_to_date = false;
|
||||||
|
|
||||||
|
for (int retry = 0; retry < 3 && g_running; retry++)
|
||||||
{
|
{
|
||||||
if ((this->*load_func)(file_to_load))
|
LOG(INFO) << "Checking update (attempt: " << (retry + 1) << "/3): " << data_name;
|
||||||
{
|
|
||||||
LOG(INFO) << "Data loaded: " + data_name;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG(INFO) << "Data invalid: " + data_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_thread_pool->push([this, file_path, etag_path, url, load_func, data_name]() {
|
try
|
||||||
file file_to_load(g_file_manager->get_project_file(file_path));
|
|
||||||
file file_etag(g_file_manager->get_project_file(etag_path));
|
|
||||||
|
|
||||||
for (int retry = 0; retry < 2; retry++)
|
|
||||||
{
|
{
|
||||||
bool ret = remote::update_binary(
|
bool ret = remote::update_binary(
|
||||||
url,
|
url,
|
||||||
file_to_load.get_path(),
|
file_to_load_path,
|
||||||
file_etag.get_path()
|
file_etag_path
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
if ((this->*load_func)(file_to_load))
|
up_to_date = true;
|
||||||
|
|
||||||
|
LOG(INFO) << "Data updated: " << data_name;
|
||||||
|
|
||||||
|
if ((this->*load_func)(file_to_load_path))
|
||||||
{
|
{
|
||||||
LOG(INFO) << "Data loaded: " + data_name;
|
LOG(INFO) << "Data loaded: " + data_name;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(INFO) << "Data invalid: " + data_name;
|
std::filesystem::remove(file_to_load_path);
|
||||||
|
std::filesystem::remove(file_etag_path);
|
||||||
try
|
|
||||||
{
|
|
||||||
std::filesystem::remove(file_to_load.get_path());
|
|
||||||
std::filesystem::remove(file_etag.get_path());
|
|
||||||
}
|
|
||||||
catch (...) { }
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (!file_to_load.exists())
|
|
||||||
{
|
|
||||||
LOG(WARNING) << "Failed to download data: " + data_name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
catch (...)
|
||||||
|
{
|
||||||
|
LOG(WARNING) << "Data invalid: " + data_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(2s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!up_to_date)
|
||||||
|
{
|
||||||
|
LOG(WARNING) << "Data not updated: " + data_name;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (file_to_load.exists())
|
||||||
|
{
|
||||||
|
if ((this->*load_func)(file_to_load_path))
|
||||||
|
{
|
||||||
|
LOG(INFO) << "Cache loaded: " + data_name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::filesystem::remove(file_to_load_path);
|
||||||
|
std::filesystem::remove(file_etag_path);
|
||||||
|
throw std::exception("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LOG(WARNING) << "Cache invalid: " + data_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gta_data_service::load_vehicles(file file_to_load)
|
bool gta_data_service::load_vehicles(std::filesystem::path path)
|
||||||
{
|
{
|
||||||
std::ifstream file(file_to_load.get_path());
|
std::ifstream file(path);
|
||||||
nlohmann::json all_vehicles;
|
nlohmann::json all_vehicles;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -235,9 +265,9 @@ namespace big
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool gta_data_service::load_peds(file file_to_load)
|
bool gta_data_service::load_peds(std::filesystem::path path)
|
||||||
{
|
{
|
||||||
std::ifstream file(file_to_load.get_path());
|
std::ifstream file(path);
|
||||||
nlohmann::json all_peds;
|
nlohmann::json all_peds;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -286,9 +316,9 @@ namespace big
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool gta_data_service::load_weapons(file file_to_load)
|
bool gta_data_service::load_weapons(std::filesystem::path path)
|
||||||
{
|
{
|
||||||
std::ifstream file(file_to_load.get_path());
|
std::ifstream file(path);
|
||||||
nlohmann::json all_weapons;
|
nlohmann::json all_weapons;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -41,11 +41,14 @@ namespace big
|
|||||||
const std::vector<weapon_item>& get_weapon_arr();
|
const std::vector<weapon_item>& get_weapon_arr();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void load_from_file(std::string file_path, std::string etag_path, std::string url, bool(gta_data_service::* load_func)(file), std::string data_name);
|
void load_from_file(
|
||||||
|
std::string file_path, std::string etag_path, std::string url,
|
||||||
|
bool(gta_data_service::* load_func)(std::filesystem::path), std::string data_name
|
||||||
|
);
|
||||||
|
|
||||||
bool load_vehicles(file file_to_load);
|
bool load_vehicles(std::filesystem::path path);
|
||||||
bool load_peds(file file_to_load);
|
bool load_peds(std::filesystem::path path);
|
||||||
bool load_weapons(file file_to_load);
|
bool load_weapons(std::filesystem::path path);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline gta_data_service* g_gta_data_service{};
|
inline gta_data_service* g_gta_data_service{};
|
||||||
|
Reference in New Issue
Block a user