Skip to content

Commit 5046257

Browse files
committed
Add Http.Get and Http.Post (closes BeamMP#55)
1 parent fcbd53f commit 5046257

File tree

2 files changed

+31
-29
lines changed

2 files changed

+31
-29
lines changed

include/TLuaEngine.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class TLuaEngine : public std::enable_shared_from_this<TLuaEngine>, IThreaded {
8282
std::shared_ptr<TLuaResult> Result;
8383
std::vector<TLuaArgTypes> Args;
8484
std::string EventName; // optional, may be empty
85-
sol::function Function;
85+
std::shared_ptr<sol::reference> FunctionRef;
8686
};
8787

8888
TLuaEngine();
@@ -211,7 +211,7 @@ class TLuaEngine : public std::enable_shared_from_this<TLuaEngine>, IThreaded {
211211
virtual ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
212212
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
213213
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
214-
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(sol::function Function, const std::vector<TLuaArgTypes>& Args);
214+
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(std::shared_ptr<sol::reference> FunctionRef, const std::vector<TLuaArgTypes>& Args);
215215
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy);
216216
void RegisterEvent(const std::string& EventName, const std::string& FunctionName);
217217
void AddPath(const fs::path& Path); // to be added to path and cpath
@@ -234,9 +234,9 @@ class TLuaEngine : public std::enable_shared_from_this<TLuaEngine>, IThreaded {
234234
sol::table Lua_GetPlayerVehicles(int ID);
235235
std::pair<sol::table, std::string> Lua_GetPositionRaw(int PID, int VID);
236236
sol::table Lua_HttpCreateConnection(const std::string& host, uint16_t port);
237-
sol::table Lua_HttpGet(std::string url, std::string path, sol::table headers, sol::function cb);
238-
sol::table Lua_HttpPost(std::string url, std::string path, sol::table body, sol::table headers, sol::function cb);
239-
void Lua_HttpCallCallback(httplib::Result& response, sol::function cb);
237+
void Lua_HttpGet(const std::string& host, const std::string& path, const sol::table& headers, const sol::function& cb);
238+
void Lua_HttpPost(const std::string& host, const std::string& path, const sol::table& body, const sol::table& headers, const sol::function& cb);
239+
void Lua_HttpCallCallback(httplib::Result& response, std::shared_ptr<sol::reference> cb);
240240
sol::table Lua_JsonDecode(const std::string& str);
241241
int Lua_GetPlayerIDByName(const std::string& Name);
242242
sol::table Lua_FS_ListFiles(const std::string& Path);

src/TLuaEngine.cpp

+26-24
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ httplib::Headers table_to_headers(const sol::table& headers) {
620620
return http_headers;
621621
}
622622

623-
void TLuaEngine::StateThreadData::Lua_HttpCallCallback(httplib::Result& response, sol::function cb) {
623+
void TLuaEngine::StateThreadData::Lua_HttpCallCallback(httplib::Result& response, std::shared_ptr<sol::reference> cb_ref) {
624624
auto args = std::vector<TLuaArgTypes>();
625625
if (response) {
626626
args.push_back(sol::nil);
@@ -636,25 +636,27 @@ void TLuaEngine::StateThreadData::Lua_HttpCallCallback(httplib::Result& response
636636
auto err = response.error();
637637
args.push_back(httplib::to_string(err));
638638
}
639-
auto res = this->EnqueueFunctionCall(cb, args);
639+
auto res = this->EnqueueFunctionCall(cb_ref, args);
640640

641641
// This doesn't seem strictly necessary, and will make threads in the http pool wait until the lua callbacks are executed
642642
res->WaitUntilReady();
643643
}
644644

645-
sol::table TLuaEngine::StateThreadData::Lua_HttpGet(std::string host, std::string path, sol::table headers, sol::function cb) {
645+
void TLuaEngine::StateThreadData::Lua_HttpGet(const std::string& host, const std::string& path, const sol::table& headers, const sol::function& cb) {
646646
auto http_headers = table_to_headers(headers);
647-
boost::asio::post(this->mEngine->http_pool, [this, host, path, http_headers, cb]() {
647+
// This method and Lua_HttpPost create a sol::reference to save the callback function in the lua registry, and then
648+
// wrap it in a shared_ptr because passing any sol object by-value involves accessing the lua state. It is NOT safe
649+
// to derefence this pointer inside the http thread pool
650+
auto cb_ref = std::make_shared<sol::reference>(cb);
651+
boost::asio::post(this->mEngine->http_pool, [this, host, path, http_headers, cb_ref]() {
648652
auto client = httplib::Client(host);
649653
client.set_follow_location(true);
650654
auto response = client.Get(path, http_headers);
651-
this->Lua_HttpCallCallback(response, cb);
655+
this->Lua_HttpCallCallback(response, cb_ref);
652656
});
653-
654-
return sol::table();
655657
}
656658

657-
sol::table TLuaEngine::StateThreadData::Lua_HttpPost(std::string host, std::string path, sol::table body, sol::table headers, sol::function cb) {
659+
void TLuaEngine::StateThreadData::Lua_HttpPost(const std::string& host, const std::string& path, const sol::table& body, const sol::table& headers, const sol::function& cb) {
658660
auto http_headers = table_to_headers(headers);
659661
httplib::Params params;
660662
for (const auto& pair : body) {
@@ -664,15 +666,13 @@ sol::table TLuaEngine::StateThreadData::Lua_HttpPost(std::string host, std::stri
664666
beammp_lua_error("Http:Get: Expected string-string pairs for headers, got something else, ignoring that header");
665667
}
666668
}
667-
668-
boost::asio::post(this->mEngine->http_pool, [this, host, path, http_headers, cb, params]() {
669+
auto cb_ref = std::make_shared<sol::reference>(cb);
670+
boost::asio::post(this->mEngine->http_pool, [this, host, path, http_headers, cb_ref, params]() {
669671
auto client = httplib::Client(host);
670672
client.set_follow_location(true);
671673
auto response = client.Post(path, http_headers, params);
672-
this->Lua_HttpCallCallback(response, cb);
674+
this->Lua_HttpCallCallback(response, cb_ref);
673675
});
674-
675-
return sol::table();
676676
}
677677

678678
sol::table TLuaEngine::StateThreadData::Lua_HttpCreateConnection(const std::string& host, uint16_t port) {
@@ -897,18 +897,18 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
897897
return Lua_HttpCreateConnection(host, port);
898898
});
899899
HttpTable.set_function("Get", sol::overload(
900-
[this](std::string url, std::string path, sol::function cb) {
900+
[this](const std::string& url, const std::string& path, const sol::function& cb) {
901901
return Lua_HttpGet(url, path, this->mStateView.create_table(), cb);
902902
},
903-
[this](std::string url, std::string path, sol::table headers, sol::function cb) {
903+
[this](const std::string& url, const std::string& path, const sol::table& headers, const sol::function& cb) {
904904
return Lua_HttpGet(url, path, headers, cb);
905905
}
906906
));
907907
HttpTable.set_function("Post", sol::overload(
908-
[this](std::string url, std::string path, sol::table body, sol::function cb) {
908+
[this](const std::string& url, const std::string& path, const sol::table& body, const sol::function& cb) {
909909
return Lua_HttpPost(url, path, body, this->mStateView.create_table(), cb);
910910
},
911-
[this](std::string url, std::string path, sol::table body, sol::table headers, sol::function cb) {
911+
[this](const std::string& url, const std::string& path, const sol::table& body, const sol::table& headers, const sol::function& cb) {
912912
return Lua_HttpPost(url, path, body, headers, cb);
913913
}
914914
));
@@ -956,6 +956,8 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueScript(const TLu
956956

957957
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy) {
958958
// TODO: Document all this
959+
// mStateFunctionQueue needs to be locked here. Calls modifying mStateFunctionQueue from other threads can invalidate this iterator mid-execution
960+
std::unique_lock Lock(mStateFunctionQueueMutex);
959961
decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end();
960962
if (Strategy == CallStrategy::BestEffort) {
961963
Iter = std::find_if(mStateFunctionQueue.begin(), mStateFunctionQueue.end(),
@@ -967,8 +969,7 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFrom
967969
auto Result = std::make_shared<TLuaResult>();
968970
Result->StateId = mStateId;
969971
Result->Function = FunctionName;
970-
std::unique_lock Lock(mStateFunctionQueueMutex);
971-
mStateFunctionQueue.push_back({ FunctionName, Result, Args, EventName, sol::nil });
972+
mStateFunctionQueue.push_back({ FunctionName, Result, Args, EventName, NULL });
972973
mStateFunctionQueueCond.notify_all();
973974
return Result;
974975
} else {
@@ -981,17 +982,17 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(con
981982
Result->StateId = mStateId;
982983
Result->Function = FunctionName;
983984
std::unique_lock Lock(mStateFunctionQueueMutex);
984-
mStateFunctionQueue.push_back({ FunctionName, Result, Args, "", sol::nil});
985+
mStateFunctionQueue.push_back({ FunctionName, Result, Args, "", NULL });
985986
mStateFunctionQueueCond.notify_all();
986987
return Result;
987988
}
988989

989-
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(sol::function function, const std::vector<TLuaArgTypes>& Args) {
990+
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(std::shared_ptr<sol::reference> FunctionRef, const std::vector<TLuaArgTypes>& Args) {
990991
auto Result = std::make_shared<TLuaResult>();
991992
Result->StateId = mStateId;
992993
Result->Function = "anonymous";
993994
std::unique_lock Lock(mStateFunctionQueueMutex);
994-
mStateFunctionQueue.push_back({ "anonymous", Result, Args, "", function});
995+
mStateFunctionQueue.push_back({ "anonymous", Result, Args, "", FunctionRef });
995996
mStateFunctionQueueCond.notify_all();
996997
return Result;
997998
}
@@ -1063,8 +1064,9 @@ void TLuaEngine::StateThreadData::operator()() {
10631064
// TODO: Use TheQueuedFunction.EventName for errors, warnings, etc
10641065
Result->StateId = mStateId;
10651066
sol::state_view StateView(mState);
1066-
auto Fn = TheQueuedFunction.Function != sol::nil ? TheQueuedFunction.Function : StateView[FnName];
1067-
if (Fn.valid() && Fn.get_type() == sol::type::function) {
1067+
auto FnRef = TheQueuedFunction.FunctionRef ? *TheQueuedFunction.FunctionRef : StateView[FnName];
1068+
if (FnRef.valid() && FnRef.get_type() == sol::type::function) {
1069+
sol::function Fn = FnRef;
10681070
std::vector<sol::object> LuaArgs;
10691071
for (const auto& Arg : Args) {
10701072
if (Arg.valueless_by_exception()) {

0 commit comments

Comments
 (0)