use iw5-script's http methods
This commit is contained in:
parent
e5b0c3e411
commit
4cba2662f7
@ -1,173 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/structs.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "http.hpp"
|
||||
#include "console.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "scripting.hpp"
|
||||
|
||||
#include <utils/http.hpp>
|
||||
#include <curl/curl.h>
|
||||
|
||||
namespace http
|
||||
{
|
||||
std::unordered_map<uint64_t, bool> active_requests{};
|
||||
uint64_t request_id{};
|
||||
|
||||
unsigned int http_get(const std::string& url)
|
||||
{
|
||||
const auto id = request_id++;
|
||||
active_requests[id] = true;
|
||||
|
||||
const auto object = scripting::array{};
|
||||
const auto object_id = object.get_entity_id();
|
||||
|
||||
scheduler::once([id, object_id, url]()
|
||||
{
|
||||
const auto data = utils::http::get_data(url);
|
||||
scheduler::once([id, object_id, data]()
|
||||
{
|
||||
if (active_requests.find(id) == active_requests.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.has_value())
|
||||
{
|
||||
scripting::notify(object_id, "done", {{}, false, "Unknown error"});
|
||||
}
|
||||
|
||||
const auto& result = data.value();
|
||||
const auto code = result.code;
|
||||
const auto error = curl_easy_strerror(code);
|
||||
|
||||
if (code != CURLE_OK)
|
||||
{
|
||||
scripting::notify(object_id, "done", {{}, false, error});
|
||||
}
|
||||
|
||||
if (result.buffer.size() >= 0x5000)
|
||||
{
|
||||
console::warn("^3WARNING: HTTP result size bigger than 20480 bytes (%i), truncating!", static_cast<int>(result.buffer.size()));
|
||||
}
|
||||
|
||||
scripting::notify(object_id, "done", {result.buffer.substr(0, 0x5000), true});
|
||||
}, scheduler::pipeline::server);
|
||||
}, scheduler::pipeline::async);
|
||||
|
||||
return object_id;
|
||||
}
|
||||
|
||||
unsigned int http_request(const std::string& url, const sol::variadic_args& va)
|
||||
{
|
||||
const auto id = request_id++;
|
||||
active_requests[id] = true;
|
||||
|
||||
const auto object = scripting::array{};
|
||||
const auto object_id = object.get_entity_id();
|
||||
|
||||
std::string fields_string{};
|
||||
std::unordered_map<std::string, std::string> headers_map{};
|
||||
|
||||
if (va.size() > 0)
|
||||
{
|
||||
const scripting::array options = va[0].as<scripting::array>();
|
||||
|
||||
const auto fields = options["parameters"];
|
||||
const auto body = options["body"];
|
||||
const auto headers = options["headers"];
|
||||
|
||||
if (fields.is<scripting::array>())
|
||||
{
|
||||
const auto fields_ = fields.as<scripting::array>();
|
||||
const auto keys = fields_.get_keys();
|
||||
|
||||
for (const auto& key : keys)
|
||||
{
|
||||
if (!key.is<std::string>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto key_ = key.as<std::string>();
|
||||
const auto value = fields_[key].to_string();
|
||||
fields_string += key_ + "=" + value + "&";
|
||||
}
|
||||
}
|
||||
|
||||
if (body.is<std::string>())
|
||||
{
|
||||
fields_string = body.as<std::string>();
|
||||
}
|
||||
|
||||
if (headers.is<scripting::array>())
|
||||
{
|
||||
const auto headers_ = headers.as<scripting::array>();
|
||||
const auto keys = headers_.get_keys();
|
||||
|
||||
for (const auto& key : keys)
|
||||
{
|
||||
if (!key.is<std::string>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto key_ = key.as<std::string>();
|
||||
const auto value = headers_[key].to_string();
|
||||
|
||||
headers_map[key_] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scheduler::once([id, object_id, url]()
|
||||
{
|
||||
const auto data = utils::http::get_data(url);
|
||||
scheduler::once([id, object_id, data]()
|
||||
{
|
||||
if (active_requests.find(id) == active_requests.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.has_value())
|
||||
{
|
||||
scripting::notify(object_id, "done", {{}, false, "Unknown error"});
|
||||
}
|
||||
|
||||
const auto& result = data.value();
|
||||
const auto code = result.code;
|
||||
const auto error = curl_easy_strerror(code);
|
||||
|
||||
if (code != CURLE_OK)
|
||||
{
|
||||
scripting::notify(object_id, "done", { {}, false, error });
|
||||
}
|
||||
|
||||
if (result.buffer.size() >= 0x5000)
|
||||
{
|
||||
console::warn("^3WARNING: HTTP result size bigger than 20480 bytes (%i), truncating!", static_cast<int>(result.buffer.size()));
|
||||
}
|
||||
|
||||
scripting::notify(object_id, "done", {result.buffer.substr(0, 0x5000), true});
|
||||
}, scheduler::pipeline::server);
|
||||
}, scheduler::pipeline::async);
|
||||
|
||||
return object_id;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
scripting::on_shutdown([]()
|
||||
{
|
||||
active_requests.clear();
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/scripting/lua/value_conversion.hpp"
|
||||
#include "game/scripting/array.hpp"
|
||||
#include "game/scripting/execution.hpp"
|
||||
|
||||
namespace http
|
||||
{
|
||||
unsigned int http_get(const std::string& url);
|
||||
unsigned int http_request(const std::string& url, const sol::variadic_args& va);
|
||||
}
|
@ -7,7 +7,6 @@
|
||||
#include "../functions.hpp"
|
||||
|
||||
#include "../../../component/command.hpp"
|
||||
#include "../../../component/http.hpp"
|
||||
#include "../../../component/logfile.hpp"
|
||||
#include "../../../component/scripting.hpp"
|
||||
#include "../../../component/fastfiles.hpp"
|
||||
@ -21,6 +20,21 @@ namespace scripting::lua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct http_request
|
||||
{
|
||||
sol::protected_function on_error;
|
||||
sol::protected_function on_progress;
|
||||
sol::protected_function on_load;
|
||||
};
|
||||
|
||||
std::unordered_map<uint64_t, http_request> http_requests;
|
||||
|
||||
std::string get_as_string(const sol::this_state s, sol::object o)
|
||||
{
|
||||
sol::state_view state(s);
|
||||
return state["tostring"](o);
|
||||
}
|
||||
|
||||
vector normalize_vector(const vector& vec)
|
||||
{
|
||||
const auto length = sqrt(
|
||||
@ -571,18 +585,160 @@ namespace scripting::lua
|
||||
}
|
||||
};
|
||||
|
||||
game_type["httpget"] = [](const game& game, const sol::this_state s,
|
||||
const std::string& url)
|
||||
static uint64_t task_id = 0;
|
||||
|
||||
state["http"] = sol::table::create(state.lua_state());
|
||||
|
||||
state["http"]["get"] = [](const sol::this_state, const std::string& url,
|
||||
const sol::protected_function& callback, sol::variadic_args va)
|
||||
{
|
||||
const auto request = http::http_get(url);
|
||||
return convert(s, scripting::entity{request});
|
||||
bool async = false;
|
||||
|
||||
if (va.size() >= 1 && va[0].get_type() == sol::type::boolean)
|
||||
{
|
||||
async = va[0].as<bool>();
|
||||
}
|
||||
|
||||
const auto cur_task_id = task_id++;
|
||||
auto request_callbacks = &http_requests[cur_task_id];
|
||||
request_callbacks->on_load = callback;
|
||||
|
||||
::scheduler::once([url, cur_task_id]()
|
||||
{
|
||||
if (http_requests.find(cur_task_id) == http_requests.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto data = utils::http::get_data(url);
|
||||
::scheduler::once([data, cur_task_id]()
|
||||
{
|
||||
if (http_requests.find(cur_task_id) == http_requests.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& request_callbacks_ = http_requests[cur_task_id];
|
||||
const auto has_value = data.has_value();
|
||||
handle_error(request_callbacks_.on_load(has_value ? data.value().buffer : "", has_value));
|
||||
http_requests.erase(cur_task_id);
|
||||
}, ::scheduler::pipeline::server);
|
||||
}, ::scheduler::pipeline::async);
|
||||
};
|
||||
|
||||
game_type["httprequest"] = [](const game& game, const sol::this_state s,
|
||||
const std::string& url, sol::variadic_args va)
|
||||
state["http"]["request"] = [](const sol::this_state s, const std::string& url, sol::variadic_args va)
|
||||
{
|
||||
const auto request = http::http_request(url, va);
|
||||
return convert(s, scripting::entity{request});
|
||||
auto request = sol::table::create(s.lua_state());
|
||||
|
||||
std::string buffer{};
|
||||
std::string fields_string{};
|
||||
std::unordered_map<std::string, std::string> headers_map;
|
||||
|
||||
if (va.size() >= 1 && va[0].get_type() == sol::type::table)
|
||||
{
|
||||
const auto options = va[0].as<sol::table>();
|
||||
|
||||
const auto fields = options["parameters"];
|
||||
const auto body = options["body"];
|
||||
const auto headers = options["headers"];
|
||||
|
||||
if (fields.get_type() == sol::type::table)
|
||||
{
|
||||
const auto _fields = fields.get<sol::table>();
|
||||
|
||||
for (const auto& field : _fields)
|
||||
{
|
||||
const auto key = field.first.as<std::string>();
|
||||
const auto value = get_as_string(s, field.second);
|
||||
|
||||
fields_string += key + "=" + value + "&";
|
||||
}
|
||||
}
|
||||
|
||||
if (body.get_type() == sol::type::string)
|
||||
{
|
||||
fields_string = body.get<std::string>();
|
||||
}
|
||||
|
||||
if (headers.get_type() == sol::type::table)
|
||||
{
|
||||
const auto _headers = headers.get<sol::table>();
|
||||
|
||||
for (const auto& header : _headers)
|
||||
{
|
||||
const auto key = header.first.as<std::string>();
|
||||
const auto value = get_as_string(s, header.second);
|
||||
|
||||
headers_map[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request["onerror"] = []() {};
|
||||
request["onprogress"] = []() {};
|
||||
request["onload"] = []() {};
|
||||
|
||||
request["send"] = [url, fields_string, request, headers_map]()
|
||||
{
|
||||
const auto cur_task_id = task_id++;
|
||||
auto request_callbacks = &http_requests[cur_task_id];
|
||||
request_callbacks->on_error = request["onerror"];
|
||||
request_callbacks->on_progress = request["onprogress"];
|
||||
request_callbacks->on_load = request["onload"];
|
||||
|
||||
::scheduler::once([url, fields_string, cur_task_id, headers_map]()
|
||||
{
|
||||
if (http_requests.find(cur_task_id) == http_requests.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = utils::http::get_data(url, fields_string, headers_map, [cur_task_id](size_t value)
|
||||
{
|
||||
::scheduler::once([cur_task_id, value]()
|
||||
{
|
||||
if (http_requests.find(cur_task_id) == http_requests.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& request_callbacks_ = http_requests[cur_task_id];
|
||||
handle_error(request_callbacks_.on_progress(value));
|
||||
}, ::scheduler::pipeline::server);
|
||||
});
|
||||
|
||||
::scheduler::once([cur_task_id, result]()
|
||||
{
|
||||
if (http_requests.find(cur_task_id) == http_requests.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& request_callbacks_ = http_requests[cur_task_id];
|
||||
|
||||
if (!result.has_value())
|
||||
{
|
||||
request_callbacks_.on_error("Unknown", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& http_result = result.value();
|
||||
|
||||
if (http_result.code == CURLE_OK)
|
||||
{
|
||||
handle_error(request_callbacks_.on_load(http_result.buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
handle_error(request_callbacks_.on_error(curl_easy_strerror(http_result.code), http_result.code));
|
||||
}
|
||||
|
||||
http_requests.erase(cur_task_id);
|
||||
}, ::scheduler::pipeline::server);
|
||||
}, ::scheduler::pipeline::async);
|
||||
};
|
||||
|
||||
return request;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,32 @@ namespace utils::http
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct progress_helper
|
||||
{
|
||||
const std::function<void(size_t)>* callback{};
|
||||
std::exception_ptr exception{};
|
||||
};
|
||||
|
||||
int progress_callback(void* clientp, const curl_off_t /*dltotal*/, const curl_off_t dlnow, const curl_off_t /*ultotal*/, const curl_off_t /*ulnow*/)
|
||||
{
|
||||
auto* helper = static_cast<progress_helper*>(clientp);
|
||||
|
||||
try
|
||||
{
|
||||
if (*helper->callback)
|
||||
{
|
||||
(*helper->callback)(dlnow);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
helper->exception = std::current_exception();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t write_callback(void* contents, const size_t size, const size_t nmemb, void* userp)
|
||||
{
|
||||
auto* buffer = static_cast<std::string*>(userp);
|
||||
@ -16,7 +42,8 @@ namespace utils::http
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<result> get_data(const std::string& url)
|
||||
std::optional<result> get_data(const std::string& url, const std::string& fields,
|
||||
const headers& headers, const std::function<void(size_t)>& callback)
|
||||
{
|
||||
curl_slist* header_list = nullptr;
|
||||
auto* curl = curl_easy_init();
|
||||
@ -31,13 +58,28 @@ namespace utils::http
|
||||
curl_easy_cleanup(curl);
|
||||
});
|
||||
|
||||
for (const auto& header : headers)
|
||||
{
|
||||
auto data = header.first + ": " + header.second;
|
||||
header_list = curl_slist_append(header_list, data.data());
|
||||
}
|
||||
|
||||
std::string buffer{};
|
||||
progress_helper helper{};
|
||||
helper.callback = &callback;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.data());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &helper);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||
|
||||
if (!fields.empty())
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, fields.data());
|
||||
}
|
||||
|
||||
const auto code = curl_easy_perform(curl);
|
||||
|
||||
@ -49,20 +91,24 @@ namespace utils::http
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
result result;
|
||||
result.code = code;
|
||||
|
||||
return result;
|
||||
if (helper.exception)
|
||||
{
|
||||
std::rethrow_exception(helper.exception);
|
||||
}
|
||||
|
||||
result result;
|
||||
result.code = code;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::future<std::optional<result>> get_data_async(const std::string& url)
|
||||
std::future<std::optional<result>> get_data_async(const std::string& url, const std::string& fields,
|
||||
const headers& headers, const std::function<void(size_t)>& callback)
|
||||
{
|
||||
return std::async(std::launch::async, [url]()
|
||||
return std::async(std::launch::async, [url, fields, headers, callback]()
|
||||
{
|
||||
return get_data(url);
|
||||
return get_data(url, fields, headers, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,14 @@ namespace utils::http
|
||||
{
|
||||
struct result
|
||||
{
|
||||
CURLcode code;
|
||||
std::string buffer;
|
||||
CURLcode code{};
|
||||
std::string buffer{};
|
||||
};
|
||||
|
||||
std::optional<result> get_data(const std::string& url);
|
||||
std::future<std::optional<result>> get_data_async(const std::string& url);
|
||||
using headers = std::unordered_map<std::string, std::string>;
|
||||
|
||||
std::optional<result> get_data(const std::string& url, const std::string& fields = {},
|
||||
const headers& headers = {}, const std::function<void(size_t)>& callback = {});
|
||||
std::future<std::optional<result>> get_data_async(const std::string& url, const std::string& fields = {},
|
||||
const headers& headers = {}, const std::function<void(size_t)>& callback = {});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user