summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/LuaState.cpp11
-rw-r--r--src/Bindings/LuaState.h24
-rw-r--r--src/Bindings/ManualBindings_Network.cpp334
-rw-r--r--src/OSSupport/TCPLinkImpl.cpp5
4 files changed, 374 insertions, 0 deletions
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index ec6bdb48a..81893c946 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -2139,6 +2139,17 @@ cLuaState * cLuaState::QueryCanonLuaState(void)
+void cLuaState::LogApiCallParamFailure(const char * a_FnName, const char * a_ParamNames)
+{
+ LOGWARNING("%s: Cannot read params: %s, bailing out.", a_FnName, a_ParamNames);
+ LogStackTrace();
+ LogStackValues("Values on the stack");
+}
+
+
+
+
+
int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
{
LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index cb68b9a98..32d346a19 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -301,6 +301,26 @@ public:
return cLuaState(m_Ref.GetLuaState()).CallTableFn(m_Ref, a_FnName, std::forward<Args>(args)...);
}
+ /** Calls the Lua function stored under the specified name in the referenced table, if still available.
+ A "self" parameter is injected in front of all the given parameters and is set to the callback table.
+ Returns true if callback has been called.
+ Returns false if the Lua state isn't valid anymore, or the function doesn't exist. */
+ template <typename... Args>
+ bool CallTableFnWithSelf(const char * a_FnName, Args &&... args)
+ {
+ auto cs = m_CS;
+ if (cs == nullptr)
+ {
+ return false;
+ }
+ cCSLock Lock(*cs);
+ if (!m_Ref.IsValid())
+ {
+ return false;
+ }
+ return cLuaState(m_Ref.GetLuaState()).CallTableFn(m_Ref, a_FnName, m_Ref, std::forward<Args>(args)...);
+ }
+
/** Set the contained reference to the table in the specified Lua state's stack position.
If another table has been previously contained, it is unreferenced first.
Returns true on success, false on failure (not a table at the specified stack pos). */
@@ -741,6 +761,10 @@ public:
Returns nullptr if the canon Lua state cannot be queried. */
cLuaState * QueryCanonLuaState(void);
+ /** Outputs to log a warning about API call being unable to read its parameters from the stack,
+ logs the stack trace and stack values. */
+ void LogApiCallParamFailure(const char * a_FnName, const char * a_ParamNames);
+
protected:
cCriticalSection m_CS;
diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp
index c8565d23d..f59860938 100644
--- a/src/Bindings/ManualBindings_Network.cpp
+++ b/src/Bindings/ManualBindings_Network.cpp
@@ -2,6 +2,7 @@
// ManualBindings_Network.cpp
// Implements the cNetwork-related API bindings for Lua
+// Also implements the cUrlClient bindings
#include "Globals.h"
#include "LuaTCPLink.h"
@@ -12,6 +13,7 @@
#include "LuaNameLookup.h"
#include "LuaServerHandle.h"
#include "LuaUDPEndpoint.h"
+#include "../HTTP/UrlClient.h"
@@ -903,6 +905,329 @@ static int tolua_cUDPEndpoint_Send(lua_State * L)
+/** Used when the cUrlClient Lua request wants all the callbacks.
+Maps each callback onto a Lua function callback in the callback table. */
+class cFullUrlClientCallbacks:
+ public cUrlClient::cCallbacks
+{
+public:
+ /** Creates a new instance bound to the specified table of callbacks. */
+ cFullUrlClientCallbacks(cLuaState::cTableRefPtr && a_Callbacks):
+ m_Callbacks(std::move(a_Callbacks))
+ {
+ }
+
+
+ // cUrlClient::cCallbacks overrides:
+ virtual void OnConnected(cTCPLink & a_Link) override
+ {
+ // TODO: Cannot push a cTCPLink to Lua, need to translate via cLuaTCPLink
+ m_Callbacks->CallTableFnWithSelf("OnConnected", cLuaState::Nil, a_Link.GetRemoteIP(), a_Link.GetRemotePort());
+ }
+
+
+ virtual bool OnCertificateReceived() override
+ {
+ // TODO: The received cert needs proper type specification from the underlying cUrlClient framework and in the Lua engine as well
+ bool res = true;
+ m_Callbacks->CallTableFnWithSelf("OnCertificateReceived", cLuaState::Return, res);
+ return res;
+ }
+
+
+ virtual void OnTlsHandshakeCompleted() override
+ {
+ m_Callbacks->CallTableFnWithSelf("OnTlsHandshakeCompleted");
+ }
+
+
+ virtual void OnRequestSent() override
+ {
+ m_Callbacks->CallTableFnWithSelf("OnRequestSent");
+ }
+
+
+ virtual void OnStatusLine(const AString & a_HttpVersion, int a_StatusCode, const AString & a_Rest) override
+ {
+ m_Callbacks->CallTableFnWithSelf("OnStatusLine", a_HttpVersion, a_StatusCode, a_Rest);
+ }
+
+
+ virtual void OnHeader(const AString & a_Key, const AString & a_Value) override
+ {
+ m_Callbacks->CallTableFnWithSelf("OnHeader", a_Key, a_Value);
+ m_Headers[a_Key] = a_Value;
+ }
+
+
+ virtual void OnHeadersFinished() override
+ {
+ m_Callbacks->CallTableFnWithSelf("OnHeadersFinished", m_Headers);
+ }
+
+
+ virtual void OnBodyData(const void * a_Data, size_t a_Size) override
+ {
+ m_Callbacks->CallTableFnWithSelf("OnBodyData", AString(reinterpret_cast<const char *>(a_Data), a_Size));
+ }
+
+
+ virtual void OnBodyFinished() override
+ {
+ m_Callbacks->CallTableFnWithSelf("OnBodyFinished");
+ }
+
+
+ virtual void OnError(const AString & a_ErrorMsg) override
+ {
+ m_Callbacks->CallTableFnWithSelf("OnError", a_ErrorMsg);
+ }
+
+
+ virtual void OnRedirecting(const AString & a_NewLocation) override
+ {
+ m_Callbacks->CallTableFnWithSelf("OnRedirecting", a_NewLocation);
+ }
+
+
+protected:
+ /** The Lua table containing the callbacks. */
+ cLuaState::cTableRefPtr m_Callbacks;
+
+ /** Accumulator for all the headers to be reported in the OnHeadersFinished() callback. */
+ AStringMap m_Headers;
+};
+
+
+
+
+
+/** Used when the cUrlClient Lua request has just a single callback.
+The callback is used to report the entire body at once, together with the HTTP headers, or to report an error:
+callback("BodyContents", {headers})
+callback(nil, "ErrorMessage")
+Accumulates the body contents into a single string until the body is finished.
+Accumulates all HTTP headers into an AStringMap. */
+class cSimpleUrlClientCallbacks:
+ public cUrlClient::cCallbacks
+{
+public:
+ /** Creates a new instance that uses the specified callback to report when request finishes. */
+ cSimpleUrlClientCallbacks(cLuaState::cCallbackPtr && a_Callback):
+ m_Callback(std::move(a_Callback))
+ {
+ }
+
+
+ virtual void OnHeader(const AString & a_Key, const AString & a_Value) override
+ {
+ m_Headers[a_Key] = a_Value;
+ }
+
+
+ virtual void OnBodyData(const void * a_Data, size_t a_Size) override
+ {
+ m_ResponseBody.append(reinterpret_cast<const char *>(a_Data), a_Size);
+ }
+
+
+ virtual void OnBodyFinished() override
+ {
+ m_Callback->Call(m_ResponseBody, m_Headers);
+ }
+
+
+ virtual void OnError(const AString & a_ErrorMsg) override
+ {
+ m_Callback->Call(cLuaState::Nil, a_ErrorMsg);
+ }
+
+
+protected:
+
+ /** The callback to call when the request finishes. */
+ cLuaState::cCallbackPtr m_Callback;
+
+ /** The accumulator for the partial body data, so that OnBodyFinished() can send the entire thing at once. */
+ AString m_ResponseBody;
+
+ /** Accumulator for all the headers to be reported in the combined callback. */
+ AStringMap m_Headers;
+};
+
+
+
+
+
+/** Common code shared among the cUrlClient request methods.
+a_Method is the method name to be used in the request.
+a_UrlStackIdx is the position on the Lua stack of the Url parameter. */
+static int tolua_cUrlClient_Request_Common(lua_State * a_LuaState, const AString & a_Method, int a_UrlStackIdx)
+{
+ // Check params:
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamString(a_UrlStackIdx))
+ {
+ return 0;
+ }
+
+ // Read params:
+ AString url, requestBody;
+ AStringMap headers, options;
+ cLuaState::cTableRefPtr callbacks;
+ cLuaState::cCallbackPtr onCompleteBodyCallback;
+ if (!L.GetStackValues(a_UrlStackIdx, url))
+ {
+ L.LogApiCallParamFailure("cUrlClient:Request()", Printf("URL (%d)", a_UrlStackIdx).c_str());
+ L.Push(false);
+ L.Push("Invalid params");
+ return 2;
+ }
+ cUrlClient::cCallbacksPtr urlClientCallbacks;
+ if (lua_istable(L, a_UrlStackIdx + 1))
+ {
+ if (!L.GetStackValue(a_UrlStackIdx + 1, callbacks))
+ {
+ L.LogApiCallParamFailure("cUrlClient:Request()", Printf("CallbacksTable (%d)", a_UrlStackIdx + 1).c_str());
+ L.Push(false);
+ L.Push("Invalid Callbacks param");
+ return 2;
+ }
+ urlClientCallbacks = cpp14::make_unique<cFullUrlClientCallbacks>(std::move(callbacks));
+ }
+ else if (lua_isfunction(L, a_UrlStackIdx + 1))
+ {
+ if (!L.GetStackValue(a_UrlStackIdx + 1, onCompleteBodyCallback))
+ {
+ L.LogApiCallParamFailure("cUrlClient:Request()", Printf("CallbacksFn (%d)", a_UrlStackIdx + 1).c_str());
+ L.Push(false);
+ L.Push("Invalid OnCompleteBodyCallback param");
+ return 2;
+ }
+ urlClientCallbacks = cpp14::make_unique<cSimpleUrlClientCallbacks>(std::move(onCompleteBodyCallback));
+ }
+ else
+ {
+ L.LogApiCallParamFailure("cUrlClient:Request()", Printf("Callbacks (%d)", a_UrlStackIdx + 1).c_str());
+ L.Push(false);
+ L.Push("Invalid OnCompleteBodyCallback param");
+ return 2;
+ }
+ if (!L.GetStackValues(a_UrlStackIdx + 2, cLuaState::cOptionalParam<AStringMap>(headers), cLuaState::cOptionalParam<AString>(requestBody), cLuaState::cOptionalParam<AStringMap>(options)))
+ {
+ L.LogApiCallParamFailure("cUrlClient:Request()", Printf("Header, Body or Options (%d, %d, %d)", a_UrlStackIdx + 2, a_UrlStackIdx + 3, a_UrlStackIdx + 4).c_str());
+ L.Push(false);
+ L.Push("Invalid params");
+ return 2;
+ }
+
+ // Make the request:
+ auto res = cUrlClient::Request(a_Method, url, std::move(urlClientCallbacks), std::move(headers), std::move(requestBody), std::move(options));
+ if (!res.first)
+ {
+ L.Push(false);
+ L.Push(res.second);
+ return 2;
+ }
+ L.Push(true);
+ return 1;
+}
+
+
+
+
+
+/** Implements cUrlClient:Get() using cUrlClient::Request(). */
+static int tolua_cUrlClient_Delete(lua_State * a_LuaState)
+{
+ /* Function signatures:
+ cUrlClient:Delete(URL, {CallbacksFnTable}, [{HeadersMapTable}], [RequestBody], [{OptionsMapTable}]) -> true / false + string
+ cUrlClient:Delete(URL, OnCompleteBodyCallback, [{HeadersMapTable}], [RequestBody], [{OptionsMapTable}]) -> true / false + string
+ */
+
+ return tolua_cUrlClient_Request_Common(a_LuaState, "DELETE", 2);
+}
+
+
+
+
+
+/** Implements cUrlClient:Get() using cUrlClient::Request(). */
+static int tolua_cUrlClient_Get(lua_State * a_LuaState)
+{
+ /* Function signatures:
+ cUrlClient:Get(URL, {CallbacksFnTable}, [{HeadersMapTable}], [RequestBody], [{OptionsMapTable}]) -> true / false + string
+ cUrlClient:Get(URL, OnCompleteBodyCallback, [{HeadersMapTable}], [RequestBody], [{OptionsMapTable}]) -> true / false + string
+ */
+
+ return tolua_cUrlClient_Request_Common(a_LuaState, "GET", 2);
+}
+
+
+
+
+
+/** Implements cUrlClient:Post() using cUrlClient::Request(). */
+static int tolua_cUrlClient_Post(lua_State * a_LuaState)
+{
+ /* Function signatures:
+ cUrlClient:Post(URL, {CallbacksFnTable}, [{HeadersMapTable}], [RequestBody], [{OptionsMapTable}]) -> true / false + string
+ cUrlClient:Post(URL, OnCompleteBodyCallback, [{HeadersMapTable}], [RequestBody], [{OptionsMapTable}]) -> true / false + string
+ */
+
+ return tolua_cUrlClient_Request_Common(a_LuaState, "POST", 2);
+}
+
+
+
+
+
+/** Implements cUrlClient:Put() using cUrlClient::Request(). */
+static int tolua_cUrlClient_Put(lua_State * a_LuaState)
+{
+ /* Function signatures:
+ cUrlClient:Put(URL, {CallbacksFnTable}, [{HeadersMapTable}], [RequestBody], [{OptionsMapTable}]) -> true / false + string
+ cUrlClient:Put(URL, OnCompleteBodyCallback, [{HeadersMapTable}], [RequestBody], [{OptionsMapTable}]) -> true / false + string
+ */
+
+ return tolua_cUrlClient_Request_Common(a_LuaState, "PUT", 2);
+}
+
+
+
+
+
+/** Binds cUrlClient::Request(). */
+static int tolua_cUrlClient_Request(lua_State * a_LuaState)
+{
+ /* Function signatures:
+ cUrlClient:Request(Method, URL, {CallbacksFnTable}, [{HeadersMapTable}], [RequestBody], [{OptionsMapTable}]) -> true / false + string
+ cUrlClient:Request(Method, URL, OnCompleteBodyCallback, [{HeadersMapTable}], [RequestBody], [{OptionsMapTable}]) -> true / false + string
+ */
+
+ // Check that the Method param is a string:
+ cLuaState L(a_LuaState);
+ if (!L.CheckParamString(2))
+ {
+ return 0;
+ }
+
+ // Redirect the rest to the common code:
+ AString method;
+ if (!L.GetStackValue(2, method))
+ {
+ L.LogApiCallParamFailure("cUrlClient:Request", "Method (2)");
+ L.Push(cLuaState::Nil);
+ L.Push("Invalid params");
+ return 2;
+ }
+ return tolua_cUrlClient_Request_Common(a_LuaState, method, 3);
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// Register the bindings:
@@ -913,10 +1238,12 @@ void cManualBindings::BindNetwork(lua_State * tolua_S)
tolua_usertype(tolua_S, "cServerHandle");
tolua_usertype(tolua_S, "cTCPLink");
tolua_usertype(tolua_S, "cUDPEndpoint");
+ tolua_usertype(tolua_S, "cUrlClient");
tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr);
tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle);
tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr);
tolua_cclass(tolua_S, "cUDPEndpoint", "cUDPEndpoint", "", tolua_collect_cUDPEndpoint);
+ tolua_cclass(tolua_S, "cUrlClient", "cUrlClient", "", nullptr);
// Fill in the functions (alpha-sorted):
tolua_beginmodule(tolua_S, "cNetwork");
@@ -953,6 +1280,13 @@ void cManualBindings::BindNetwork(lua_State * tolua_S)
tolua_function(tolua_S, "Send", tolua_cUDPEndpoint_Send);
tolua_endmodule(tolua_S);
+ tolua_beginmodule(tolua_S, "cUrlClient");
+ tolua_function(tolua_S, "Delete", tolua_cUrlClient_Delete);
+ tolua_function(tolua_S, "Get", tolua_cUrlClient_Get);
+ tolua_function(tolua_S, "Post", tolua_cUrlClient_Post);
+ tolua_function(tolua_S, "Put", tolua_cUrlClient_Put);
+ tolua_function(tolua_S, "Request", tolua_cUrlClient_Request);
+ tolua_endmodule(tolua_S);
}
diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp
index df70f3f72..d55dc9da1 100644
--- a/src/OSSupport/TCPLinkImpl.cpp
+++ b/src/OSSupport/TCPLinkImpl.cpp
@@ -322,6 +322,11 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void
{
ASSERT(a_Self != nullptr);
cTCPLinkImplPtr Self = static_cast<cTCPLinkImpl *>(a_Self)->m_Self;
+ if (Self == nullptr)
+ {
+ // The link has already been freed
+ return;
+ }
// If an error is reported, call the error callback:
if (a_What & BEV_EVENT_ERROR)