summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/CMakeLists.txt7
-rw-r--r--src/Bindings/LuaNameLookup.cpp88
-rw-r--r--src/Bindings/LuaNameLookup.h46
-rw-r--r--src/Bindings/LuaServerHandle.cpp209
-rw-r--r--src/Bindings/LuaServerHandle.h89
-rw-r--r--src/Bindings/LuaState.cpp45
-rw-r--r--src/Bindings/LuaState.h11
-rw-r--r--src/Bindings/LuaTCPLink.cpp304
-rw-r--r--src/Bindings/LuaTCPLink.h97
-rw-r--r--src/Bindings/ManualBindings.cpp3
-rw-r--r--src/Bindings/ManualBindings.h10
-rw-r--r--src/Bindings/ManualBindings_Network.cpp513
-rw-r--r--src/Globals.h1
-rw-r--r--src/OSSupport/Network.h3
-rw-r--r--src/OSSupport/TCPLinkImpl.cpp4
15 files changed, 1427 insertions, 3 deletions
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
index d47579cd6..40b8d4bd9 100644
--- a/src/Bindings/CMakeLists.txt
+++ b/src/Bindings/CMakeLists.txt
@@ -8,9 +8,13 @@ SET (SRCS
Bindings.cpp
DeprecatedBindings.cpp
LuaChunkStay.cpp
+ LuaNameLookup.cpp
+ LuaServerHandle.cpp
LuaState.cpp
+ LuaTCPLink.cpp
LuaWindow.cpp
ManualBindings.cpp
+ ManualBindings_Network.cpp
ManualBindings_RankManager.cpp
Plugin.cpp
PluginLua.cpp
@@ -23,7 +27,10 @@ SET (HDRS
DeprecatedBindings.h
LuaChunkStay.h
LuaFunctions.h
+ LuaNameLookup.h
+ LuaServerHandle.h
LuaState.h
+ LuaTCPLink.h
LuaWindow.h
ManualBindings.h
Plugin.h
diff --git a/src/Bindings/LuaNameLookup.cpp b/src/Bindings/LuaNameLookup.cpp
new file mode 100644
index 000000000..e52d8dbdc
--- /dev/null
+++ b/src/Bindings/LuaNameLookup.cpp
@@ -0,0 +1,88 @@
+
+// LuaNameLookup.cpp
+
+// Implements the cLuaNameLookup class used as the cNetwork API callbacks for name and IP lookups from Lua
+
+#include "Globals.h"
+#include "LuaNameLookup.h"
+
+
+
+
+
+cLuaNameLookup::cLuaNameLookup(const AString & a_Query, cPluginLua & a_Plugin, int a_CallbacksTableStackPos):
+ m_Plugin(a_Plugin),
+ m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos),
+ m_Query(a_Query)
+{
+}
+
+
+
+
+
+void cLuaNameLookup::OnNameResolved(const AString & a_Name, const AString & a_IP)
+{
+ // Check if we're still valid:
+ if (!m_Callbacks.IsValid())
+ {
+ return;
+ }
+
+ // Call the callback:
+ cPluginLua::cOperation Op(m_Plugin);
+ if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnNameResolved"), a_Name, a_IP))
+ {
+ LOGINFO("cNetwork name lookup OnNameResolved callback failed in plugin %s looking up %s. %s resolves to %s.",
+ m_Plugin.GetName().c_str(), m_Query.c_str(), a_Name.c_str(), a_IP.c_str()
+ );
+ }
+}
+
+
+
+
+
+void cLuaNameLookup::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
+{
+ // Check if we're still valid:
+ if (!m_Callbacks.IsValid())
+ {
+ return;
+ }
+
+ // Call the callback:
+ cPluginLua::cOperation Op(m_Plugin);
+ if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnError"), m_Query, a_ErrorCode, a_ErrorMsg))
+ {
+ LOGINFO("cNetwork name lookup OnError callback failed in plugin %s looking up %s. The error is %d (%s)",
+ m_Plugin.GetName().c_str(), m_Query.c_str(), a_ErrorCode, a_ErrorMsg.c_str()
+ );
+ }
+}
+
+
+
+
+
+void cLuaNameLookup::OnFinished(void)
+{
+ // Check if we're still valid:
+ if (!m_Callbacks.IsValid())
+ {
+ return;
+ }
+
+ // Call the callback:
+ cPluginLua::cOperation Op(m_Plugin);
+ if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnFinished"), m_Query))
+ {
+ LOGINFO("cNetwork name lookup OnFinished callback failed in plugin %s, looking up %s.",
+ m_Plugin.GetName().c_str(), m_Query.c_str()
+ );
+ }
+}
+
+
+
+
diff --git a/src/Bindings/LuaNameLookup.h b/src/Bindings/LuaNameLookup.h
new file mode 100644
index 000000000..e4cdb9f53
--- /dev/null
+++ b/src/Bindings/LuaNameLookup.h
@@ -0,0 +1,46 @@
+
+// LuaNameLookup.h
+
+// Declares the cLuaNameLookup class used as the cNetwork API callbacks for name and IP lookups from Lua
+
+
+
+
+
+#pragma once
+
+#include "../OSSupport/Network.h"
+#include "PluginLua.h"
+
+
+
+
+
+class cLuaNameLookup:
+ public cNetwork::cResolveNameCallbacks
+{
+public:
+ /** Creates a new instance of the lookup callbacks for the specified query,
+ attached to the specified lua plugin and wrapping the callbacks that are in a table at the specified stack pos. */
+ cLuaNameLookup(const AString & a_Query, cPluginLua & a_Plugin, int a_CallbacksTableStackPos);
+
+protected:
+ /** The plugin for which the query is created. */
+ cPluginLua & m_Plugin;
+
+ /** The Lua table that holds the callbacks to be invoked. */
+ cLuaState::cRef m_Callbacks;
+
+ /** The query used to start the lookup (either hostname or IP). */
+ AString m_Query;
+
+
+ // cNetwork::cResolveNameCallbacks overrides:
+ virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) override;
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override;
+ virtual void OnFinished(void) override;
+};
+
+
+
+
diff --git a/src/Bindings/LuaServerHandle.cpp b/src/Bindings/LuaServerHandle.cpp
new file mode 100644
index 000000000..a84f894b5
--- /dev/null
+++ b/src/Bindings/LuaServerHandle.cpp
@@ -0,0 +1,209 @@
+
+// LuaServerHandle.cpp
+
+// Implements the cLuaServerHandle class wrapping the cServerHandle functionality for Lua API
+
+#include "Globals.h"
+#include "LuaServerHandle.h"
+#include "LuaTCPLink.h"
+#include "tolua++/include/tolua++.h"
+
+
+
+
+
+cLuaServerHandle::cLuaServerHandle(UInt16 a_Port, cPluginLua & a_Plugin, int a_CallbacksTableStackPos):
+ m_Plugin(a_Plugin),
+ m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos),
+ m_Port(a_Port)
+{
+}
+
+
+
+
+
+
+cLuaServerHandle::~cLuaServerHandle()
+{
+ // If the server handle is still open, close it explicitly:
+ Close();
+}
+
+
+
+
+
+void cLuaServerHandle::SetServerHandle(cServerHandlePtr a_ServerHandle, cLuaServerHandlePtr a_Self)
+{
+ ASSERT(m_ServerHandle == nullptr); // The handle can be set only once
+
+ m_ServerHandle = a_ServerHandle;
+ m_Self = a_Self;
+}
+
+
+
+
+
+void cLuaServerHandle::Close(void)
+{
+ // Safely grab a copy of the server handle:
+ cServerHandlePtr ServerHandle = m_ServerHandle;
+ if (ServerHandle == nullptr)
+ {
+ return;
+ }
+
+ // Close the underlying server handle:
+ ServerHandle->Close();
+
+ // Close all connections at this server:
+ cLuaTCPLinkPtrs Connections;
+ {
+ cCSLock Lock(m_CSConnections);
+ std::swap(Connections, m_Connections);
+ }
+ for (auto & conn: Connections)
+ {
+ conn->Close();
+ }
+ Connections.clear();
+
+ // Allow the internal server handle to be deallocated:
+ m_ServerHandle.reset();
+}
+
+
+
+
+
+bool cLuaServerHandle::IsListening(void)
+{
+ // Safely grab a copy of the server handle:
+ cServerHandlePtr ServerHandle = m_ServerHandle;
+ if (ServerHandle == nullptr)
+ {
+ return false;
+ }
+
+ // Query the underlying server handle:
+ return ServerHandle->IsListening();
+}
+
+
+
+
+
+void cLuaServerHandle::RemoveLink(cLuaTCPLink * a_Link)
+{
+ cCSLock Lock(m_CSConnections);
+ for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr)
+ {
+ if (itr->get() == a_Link)
+ {
+ m_Connections.erase(itr);
+ break;
+ }
+ } // for itr - m_Connections[]
+}
+
+
+
+
+
+void cLuaServerHandle::Release(void)
+{
+ // Close the server, if it isn't closed yet:
+ Close();
+
+ // Allow self to deallocate:
+ m_Self.reset();
+}
+
+
+
+
+
+cTCPLink::cCallbacksPtr cLuaServerHandle::OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort)
+{
+ // If not valid anymore, drop the connection:
+ if (!m_Callbacks.IsValid())
+ {
+ return nullptr;
+ }
+
+ // Ask the plugin for link callbacks:
+ cPluginLua::cOperation Op(m_Plugin);
+ cLuaState::cRef LinkCallbacks;
+ if (
+ !Op().Call(cLuaState::cTableRef(m_Callbacks, "OnIncomingConnection"), a_RemoteIPAddress, a_RemotePort, m_Port, cLuaState::Return, LinkCallbacks) ||
+ !LinkCallbacks.IsValid()
+ )
+ {
+ LOGINFO("cNetwork server (port %d) OnIncomingConnection callback failed in plugin %s. Dropping connection.",
+ m_Port, m_Plugin.GetName().c_str()
+ );
+ return nullptr;
+ }
+
+ // Create the link wrapper to use with the callbacks:
+ auto res = std::make_shared<cLuaTCPLink>(m_Plugin, std::move(LinkCallbacks), m_Self);
+
+ // Add the link to the list of our connections:
+ cCSLock Lock(m_CSConnections);
+ m_Connections.push_back(res);
+
+ return res;
+}
+
+
+
+
+
+void cLuaServerHandle::OnAccepted(cTCPLink & a_Link)
+{
+ // Check if we're still valid:
+ if (!m_Callbacks.IsValid())
+ {
+ return;
+ }
+
+ // Notify the plugin:
+ cPluginLua::cOperation Op(m_Plugin);
+ if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnAccepted"), static_cast<cLuaTCPLink *>(a_Link.GetCallbacks().get())))
+ {
+ LOGINFO("cNetwork server (port %d) OnAccepted callback failed in plugin %s, connection to %s:%d.",
+ m_Port, m_Plugin.GetName().c_str(), a_Link.GetRemoteIP().c_str(), a_Link.GetRemotePort()
+ );
+ return;
+ }
+}
+
+
+
+
+
+void cLuaServerHandle::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
+{
+ // Check if we're still valid:
+ if (!m_Callbacks.IsValid())
+ {
+ return;
+ }
+
+ // Notify the plugin:
+ cPluginLua::cOperation Op(m_Plugin);
+ if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnError"), a_ErrorCode, a_ErrorMsg))
+ {
+ LOGINFO("cNetwork server (port %d) OnError callback failed in plugin %s. The error is %d (%s).",
+ m_Port, m_Plugin.GetName().c_str(), a_ErrorCode, a_ErrorMsg.c_str()
+ );
+ return;
+ }
+}
+
+
+
+
+
diff --git a/src/Bindings/LuaServerHandle.h b/src/Bindings/LuaServerHandle.h
new file mode 100644
index 000000000..9325bca3e
--- /dev/null
+++ b/src/Bindings/LuaServerHandle.h
@@ -0,0 +1,89 @@
+
+// LuaServerHandle.h
+
+// Declares the cLuaServerHandle class wrapping the cServerHandle functionality for Lua API
+
+
+
+
+
+#pragma once
+
+#include "../OSSupport/Network.h"
+#include "PluginLua.h"
+
+
+
+
+
+// fwd:
+class cLuaTCPLink;
+typedef SharedPtr<cLuaTCPLink> cLuaTCPLinkPtr;
+typedef std::vector<cLuaTCPLinkPtr> cLuaTCPLinkPtrs;
+class cLuaServerHandle;
+typedef SharedPtr<cLuaServerHandle> cLuaServerHandlePtr;
+
+
+
+
+class cLuaServerHandle:
+ public cNetwork::cListenCallbacks
+{
+public:
+ /** Creates a new instance of the server handle,
+ attached to the specified lua plugin and wrapping the (listen-) callbacks that are in a table at the specified stack pos. */
+ cLuaServerHandle(UInt16 a_Port, cPluginLua & a_Plugin, int a_CallbacksTableStackPos);
+
+ ~cLuaServerHandle();
+
+ /** Called by cNetwork::Listen()'s binding.
+ Sets the server handle around which this instance is wrapped, and a self SharedPtr for link management. */
+ void SetServerHandle(cServerHandlePtr a_ServerHandle, cLuaServerHandlePtr a_Self);
+
+ /** Terminates all connections and closes the listening socket. */
+ void Close(void);
+
+ /** Returns true if the server is currently listening. */
+ bool IsListening(void);
+
+ /** Removes the link from the list of links that the server is currently tracking. */
+ void RemoveLink(cLuaTCPLink * a_Link);
+
+ /** Called when Lua garbage-collects the object.
+ Releases the internal SharedPtr to self, so that the instance may be deallocated. */
+ void Release(void);
+
+protected:
+ /** The plugin for which the server is created. */
+ cPluginLua & m_Plugin;
+
+ /** The Lua table that holds the callbacks to be invoked. */
+ cLuaState::cRef m_Callbacks;
+
+ /** The port on which the server is listening.
+ Used mainly for better error reporting. */
+ UInt16 m_Port;
+
+ /** The cServerHandle around which this instance is wrapped. */
+ cServerHandlePtr m_ServerHandle;
+
+ /** Protects m_Connections against multithreaded access. */
+ cCriticalSection m_CSConnections;
+
+ /** All connections that are currently active in this server.
+ Protected by m_CSConnections. */
+ cLuaTCPLinkPtrs m_Connections;
+
+ /** SharedPtr to self, given out to newly created links. */
+ cLuaServerHandlePtr m_Self;
+
+
+ // cNetwork::cListenCallbacks overrides:
+ virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override;
+ virtual void OnAccepted(cTCPLink & a_Link) override;
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override;
+};
+
+
+
+
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index 01d3ac687..73b114599 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -656,6 +656,30 @@ void cLuaState::Push(cItems * a_Items)
+void cLuaState::Push(cLuaServerHandle * a_ServerHandle)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_ServerHandle, "cServerHandle");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(cLuaTCPLink * a_TCPLink)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, a_TCPLink, "cTCPLink");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
void cLuaState::Push(cMonster * a_Monster)
{
ASSERT(IsValid());
@@ -958,6 +982,15 @@ void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal)
+void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref)
+{
+ a_Ref.RefStack(*this, a_StackPos);
+}
+
+
+
+
+
bool cLuaState::CallFunction(int a_NumResults)
{
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
@@ -1527,6 +1560,18 @@ cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
+cLuaState::cRef::cRef(cRef && a_FromRef):
+ m_LuaState(a_FromRef.m_LuaState),
+ m_Ref(a_FromRef.m_Ref)
+{
+ a_FromRef.m_LuaState = nullptr;
+ a_FromRef.m_Ref = LUA_REFNIL;
+}
+
+
+
+
+
cLuaState::cRef::~cRef()
{
if (m_LuaState != nullptr)
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index 97e6b47e1..f68b022ea 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -59,6 +59,8 @@ class cTNTEntity;
class cHopperEntity;
class cBlockEntity;
class cBoundingBox;
+class cLuaTCPLink;
+class cLuaServerHandle;
typedef cBoundingBox * pBoundingBox;
typedef cWorld * pWorld;
@@ -83,6 +85,10 @@ public:
/** Creates a reference in the specified LuaState for object at the specified StackPos */
cRef(cLuaState & a_LuaState, int a_StackPos);
+
+ /** Moves the reference from the specified instance into a newly created instance.
+ The old instance is then "!IsValid()". */
+ cRef(cRef && a_FromRef);
~cRef();
@@ -202,6 +208,8 @@ public:
void Push(cHopperEntity * a_Hopper);
void Push(cItem * a_Item);
void Push(cItems * a_Items);
+ void Push(cLuaServerHandle * a_ServerHandle);
+ void Push(cLuaTCPLink * a_TCPLink);
void Push(cMonster * a_Monster);
void Push(cPickup * a_Pickup);
void Push(cPlayer * a_Player);
@@ -240,6 +248,9 @@ public:
/** Retrieve value at a_StackPos, if it is a valid cWorld class. If not, a_Value is unchanged */
void GetStackValue(int a_StackPos, pWorld & a_Value);
+
+ /** Store the value at a_StackPos as a reference. */
+ void GetStackValue(int a_StackPos, cRef & a_Ref);
/** Call the specified Lua function.
Returns true if call succeeded, false if there was an error.
diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp
new file mode 100644
index 000000000..6b8395806
--- /dev/null
+++ b/src/Bindings/LuaTCPLink.cpp
@@ -0,0 +1,304 @@
+
+// LuaTCPLink.cpp
+
+// Implements the cLuaTCPLink class representing a Lua wrapper for the cTCPLink class and the callbacks it needs
+
+#include "Globals.h"
+#include "LuaTCPLink.h"
+#include "LuaServerHandle.h"
+
+
+
+
+
+cLuaTCPLink::cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos):
+ m_Plugin(a_Plugin),
+ m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos)
+{
+ // Warn if the callbacks aren't valid:
+ if (!m_Callbacks.IsValid())
+ {
+ LOGWARNING("cTCPLink in plugin %s: callbacks could not be retrieved", m_Plugin.GetName().c_str());
+ cPluginLua::cOperation Op(m_Plugin);
+ Op().LogStackTrace();
+ }
+}
+
+
+
+
+
+cLuaTCPLink::cLuaTCPLink(cPluginLua & a_Plugin, cLuaState::cRef && a_CallbacksTableRef, cLuaServerHandleWPtr a_ServerHandle):
+ m_Plugin(a_Plugin),
+ m_Callbacks(std::move(a_CallbacksTableRef)),
+ m_Server(std::move(a_ServerHandle))
+{
+ // Warn if the callbacks aren't valid:
+ if (!m_Callbacks.IsValid())
+ {
+ LOGWARNING("cTCPLink in plugin %s: callbacks could not be retrieved", m_Plugin.GetName().c_str());
+ cPluginLua::cOperation Op(m_Plugin);
+ Op().LogStackTrace();
+ }
+}
+
+
+
+
+
+cLuaTCPLink::~cLuaTCPLink()
+{
+ // If the link is still open, close it:
+ cTCPLinkPtr Link = m_Link;
+ if (Link != nullptr)
+ {
+ Link->Close();
+ }
+
+ Terminated();
+}
+
+
+
+
+
+bool cLuaTCPLink::Send(const AString & a_Data)
+{
+ // Safely grab a copy of the link:
+ cTCPLinkPtr Link = m_Link;
+ if (Link == nullptr)
+ {
+ return false;
+ }
+
+ // Send the data:
+ return Link->Send(a_Data);
+}
+
+
+
+
+
+AString cLuaTCPLink::GetLocalIP(void) const
+{
+ // Safely grab a copy of the link:
+ cTCPLinkPtr Link = m_Link;
+ if (Link == nullptr)
+ {
+ return "";
+ }
+
+ // Get the IP address:
+ return Link->GetLocalIP();
+}
+
+
+
+
+
+UInt16 cLuaTCPLink::GetLocalPort(void) const
+{
+ // Safely grab a copy of the link:
+ cTCPLinkPtr Link = m_Link;
+ if (Link == nullptr)
+ {
+ return 0;
+ }
+
+ // Get the port:
+ return Link->GetLocalPort();
+}
+
+
+
+
+
+AString cLuaTCPLink::GetRemoteIP(void) const
+{
+ // Safely grab a copy of the link:
+ cTCPLinkPtr Link = m_Link;
+ if (Link == nullptr)
+ {
+ return "";
+ }
+
+ // Get the IP address:
+ return Link->GetRemoteIP();
+}
+
+
+
+
+
+UInt16 cLuaTCPLink::GetRemotePort(void) const
+{
+ // Safely grab a copy of the link:
+ cTCPLinkPtr Link = m_Link;
+ if (Link == nullptr)
+ {
+ return 0;
+ }
+
+ // Get the port:
+ return Link->GetRemotePort();
+}
+
+
+
+
+
+void cLuaTCPLink::Shutdown(void)
+{
+ // Safely grab a copy of the link and shut it down:
+ cTCPLinkPtr Link = m_Link;
+ if (Link != nullptr)
+ {
+ Link->Shutdown();
+ }
+
+ Terminated();
+}
+
+
+
+
+
+void cLuaTCPLink::Close(void)
+{
+ // If the link is still open, close it:
+ cTCPLinkPtr Link = m_Link;
+ if (Link != nullptr)
+ {
+ Link->Close();
+ }
+
+ Terminated();
+}
+
+
+
+
+
+void cLuaTCPLink::Terminated(void)
+{
+ // Disable the callbacks:
+ if (m_Callbacks.IsValid())
+ {
+ m_Callbacks.UnRef();
+ }
+
+ // If the managing server is still alive, let it know we're terminating:
+ auto Server = m_Server.lock();
+ if (Server != nullptr)
+ {
+ Server->RemoveLink(this);
+ }
+
+ // If the link is still open, close it:
+ cTCPLinkPtr Link = m_Link;
+ if (Link != nullptr)
+ {
+ Link->Close();
+ m_Link.reset();
+ }
+}
+
+
+
+
+
+void cLuaTCPLink::OnConnected(cTCPLink & a_Link)
+{
+ // Check if we're still valid:
+ if (!m_Callbacks.IsValid())
+ {
+ return;
+ }
+
+ // Call the callback:
+ cPluginLua::cOperation Op(m_Plugin);
+ if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnConnected"), this))
+ {
+ LOGINFO("cTCPLink OnConnected() callback failed in plugin %s.", m_Plugin.GetName().c_str());
+ }
+}
+
+
+
+
+
+void cLuaTCPLink::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
+{
+ // Check if we're still valid:
+ if (!m_Callbacks.IsValid())
+ {
+ return;
+ }
+
+ // Call the callback:
+ cPluginLua::cOperation Op(m_Plugin);
+ if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnError"), this, a_ErrorCode, a_ErrorMsg))
+ {
+ LOGINFO("cTCPLink OnError() callback failed in plugin %s; the link error is %d (%s).",
+ m_Plugin.GetName().c_str(), a_ErrorCode, a_ErrorMsg.c_str()
+ );
+ }
+
+ Terminated();
+}
+
+
+
+
+
+void cLuaTCPLink::OnLinkCreated(cTCPLinkPtr a_Link)
+{
+ // Store the cTCPLink for later use:
+ m_Link = a_Link;
+}
+
+
+
+
+
+void cLuaTCPLink::OnReceivedData(const char * a_Data, size_t a_Length)
+{
+ // Check if we're still valid:
+ if (!m_Callbacks.IsValid())
+ {
+ return;
+ }
+
+ // Call the callback:
+ cPluginLua::cOperation Op(m_Plugin);
+ if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnReceivedData"), this, AString(a_Data, a_Length)))
+ {
+ LOGINFO("cTCPLink OnReceivedData callback failed in plugin %s.", m_Plugin.GetName().c_str());
+ }
+}
+
+
+
+
+
+void cLuaTCPLink::OnRemoteClosed(void)
+{
+ // Check if we're still valid:
+ if (!m_Callbacks.IsValid())
+ {
+ return;
+ }
+
+ // Call the callback:
+ cPluginLua::cOperation Op(m_Plugin);
+ if (!Op().Call(cLuaState::cTableRef(m_Callbacks, "OnRemoteClosed"), this))
+ {
+ LOGINFO("cTCPLink OnRemoteClosed() callback failed in plugin %s.", m_Plugin.GetName().c_str());
+ }
+
+ Terminated();
+}
+
+
+
+
diff --git a/src/Bindings/LuaTCPLink.h b/src/Bindings/LuaTCPLink.h
new file mode 100644
index 000000000..f2af911ec
--- /dev/null
+++ b/src/Bindings/LuaTCPLink.h
@@ -0,0 +1,97 @@
+
+// LuaTCPLink.h
+
+// Declares the cLuaTCPLink class representing a Lua wrapper for the cTCPLink class and the callbacks it needs
+
+
+
+
+
+#pragma once
+
+#include "../OSSupport/Network.h"
+#include "PluginLua.h"
+
+
+
+
+
+// fwd:
+class cLuaServerHandle;
+typedef WeakPtr<cLuaServerHandle> cLuaServerHandleWPtr;
+
+
+
+
+
+class cLuaTCPLink:
+ public cNetwork::cConnectCallbacks,
+ public cTCPLink::cCallbacks
+{
+public:
+ /** Creates a new instance of the link, attached to the specified plugin and wrapping the callbacks that are in a table at the specified stack pos. */
+ cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos);
+
+ /** Creates a new instance of the link, attached to the specified plugin and wrapping the callbacks that are in the specified referenced table. */
+ cLuaTCPLink(cPluginLua & a_Plugin, cLuaState::cRef && a_CallbacksTableRef, cLuaServerHandleWPtr a_Server);
+
+ ~cLuaTCPLink();
+
+ /** Sends the data contained in the string to the remote peer.
+ Returns true if successful, false on immediate failure (queueing the data failed or link not available). */
+ bool Send(const AString & a_Data);
+
+ /** Returns the IP address of the local endpoint of the connection. */
+ AString GetLocalIP(void) const;
+
+ /** Returns the port used by the local endpoint of the connection. */
+ UInt16 GetLocalPort(void) const;
+
+ /** Returns the IP address of the remote endpoint of the connection. */
+ AString GetRemoteIP(void) const;
+
+ /** Returns the port used by the remote endpoint of the connection. */
+ UInt16 GetRemotePort(void) const;
+
+ /** Closes the link gracefully.
+ The link will send any queued outgoing data, then it will send the FIN packet.
+ The link will still receive incoming data from remote until the remote closes the connection. */
+ void Shutdown(void);
+
+ /** Drops the connection without any more processing.
+ Sends the RST packet, queued outgoing and incoming data is lost. */
+ void Close(void);
+
+protected:
+ /** The plugin for which the link is created. */
+ cPluginLua & m_Plugin;
+
+ /** The Lua table that holds the callbacks to be invoked. */
+ cLuaState::cRef m_Callbacks;
+
+ /** The underlying link representing the connection.
+ May be nullptr. */
+ cTCPLinkPtr m_Link;
+
+ /** The server that is responsible for this link, if any. */
+ cLuaServerHandleWPtr m_Server;
+
+
+ /** Common code called when the link is considered as terminated.
+ Releases m_Link, m_Callbacks and this from m_Server, each when applicable. */
+ void Terminated(void);
+
+ // cNetwork::cConnectCallbacks overrides:
+ virtual void OnConnected(cTCPLink & a_Link) override;
+ virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override;
+
+ // cTCPLink::cCallbacks overrides:
+ virtual void OnLinkCreated(cTCPLinkPtr a_Link) override;
+ virtual void OnReceivedData(const char * a_Data, size_t a_Length) override;
+ virtual void OnRemoteClosed(void) override;
+ // The OnError() callback is shared with cNetwork::cConnectCallbacks
+};
+
+
+
+
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 56f2e73bc..24e3f0052 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -256,7 +256,7 @@ static int tolua_Base64Decode(lua_State * tolua_S)
-static cPluginLua * GetLuaPlugin(lua_State * L)
+cPluginLua * GetLuaPlugin(lua_State * L)
{
// Get the plugin identification out of LuaState:
lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME);
@@ -3556,6 +3556,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "md5", tolua_md5);
BindRankManager(tolua_S);
+ BindNetwork(tolua_S);
tolua_endmodule(tolua_S);
}
diff --git a/src/Bindings/ManualBindings.h b/src/Bindings/ManualBindings.h
index 1b6e65654..74d24d5f5 100644
--- a/src/Bindings/ManualBindings.h
+++ b/src/Bindings/ManualBindings.h
@@ -1,6 +1,7 @@
#pragma once
struct lua_State;
+class cPluginLua;
@@ -17,8 +18,17 @@ protected:
/** Binds the manually implemented cRankManager glue code to tolua_S.
Implemented in ManualBindings_RankManager.cpp. */
static void BindRankManager(lua_State * tolua_S);
+
+ /** Binds the manually implemented cNetwork-related API to tolua_S.
+ Implemented in ManualBindings_Network.cpp. */
+ static void BindNetwork(lua_State * tolua_S);
};
+extern cPluginLua * GetLuaPlugin(lua_State * L);
+
+
+
+
diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp
new file mode 100644
index 000000000..902f687c8
--- /dev/null
+++ b/src/Bindings/ManualBindings_Network.cpp
@@ -0,0 +1,513 @@
+
+// ManualBindings_Network.cpp
+
+// Implements the cNetwork-related API bindings for Lua
+
+#include "Globals.h"
+#include "LuaTCPLink.h"
+#include "ManualBindings.h"
+#include "tolua++/include/tolua++.h"
+#include "LuaState.h"
+#include "LuaTCPLink.h"
+#include "LuaNameLookup.h"
+#include "LuaServerHandle.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cNetwork API functions:
+
+/** Binds cNetwork::Connect */
+static int tolua_cNetwork_Connect(lua_State * L)
+{
+ // Function signature:
+ // cNetwork:Connect(Host, Port, Callbacks) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cNetwork") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamNumber(3) ||
+ !S.CheckParamTable(4) ||
+ !S.CheckParamEnd(5)
+ )
+ {
+ return 0;
+ }
+
+ // Get the plugin instance:
+ cPluginLua * Plugin = GetLuaPlugin(L);
+ if (Plugin == nullptr)
+ {
+ // An error message has been already printed in GetLuaPlugin()
+ S.Push(false);
+ return 1;
+ }
+
+ // Read the params:
+ AString Host;
+ int Port;
+ S.GetStackValues(2, Host, Port);
+
+ // Check validity:
+ if ((Port < 0) || (Port > 65535))
+ {
+ LOGWARNING("cNetwork:Connect() called with invalid port (%d), failing the request.", Port);
+ S.Push(false);
+ return 1;
+ }
+
+ // Create the LuaTCPLink glue class:
+ auto Link = std::make_shared<cLuaTCPLink>(*Plugin, 4);
+
+ // Try to connect:
+ bool res = cNetwork::Connect(Host, static_cast<UInt16>(Port), Link, Link);
+ S.Push(res);
+
+ return 1;
+}
+
+
+
+
+
+/** Binds cNetwork::HostnameToIP */
+static int tolua_cNetwork_HostnameToIP(lua_State * L)
+{
+ // Function signature:
+ // cNetwork:HostnameToIP(Host, Callbacks) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cNetwork") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamTable(3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the plugin instance:
+ cPluginLua * Plugin = GetLuaPlugin(L);
+ if (Plugin == nullptr)
+ {
+ // An error message has been already printed in GetLuaPlugin()
+ S.Push(false);
+ return 1;
+ }
+
+ // Read the params:
+ AString Host;
+ S.GetStackValue(2, Host);
+
+ // Try to look up:
+ bool res = cNetwork::HostnameToIP(Host, std::make_shared<cLuaNameLookup>(Host, *Plugin, 3));
+ S.Push(res);
+
+ return 1;
+}
+
+
+
+
+
+/** Binds cNetwork::IPToHostname */
+static int tolua_cNetwork_IPToHostname(lua_State * L)
+{
+ // Function signature:
+ // cNetwork:IPToHostname(IP, Callbacks) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cNetwork") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamTable(3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the plugin instance:
+ cPluginLua * Plugin = GetLuaPlugin(L);
+ if (Plugin == nullptr)
+ {
+ // An error message has been already printed in GetLuaPlugin()
+ S.Push(false);
+ return 1;
+ }
+
+ // Read the params:
+ AString Host;
+ S.GetStackValue(2, Host);
+
+ // Try to look up:
+ bool res = cNetwork::IPToHostName(Host, std::make_shared<cLuaNameLookup>(Host, *Plugin, 3));
+ S.Push(res);
+
+ return 1;
+}
+
+
+
+
+
+/** Binds cNetwork::Listen */
+static int tolua_cNetwork_Listen(lua_State * L)
+{
+ // Function signature:
+ // cNetwork:Listen(Port, Callbacks) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cNetwork") ||
+ !S.CheckParamNumber(2) ||
+ !S.CheckParamTable(3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the plugin instance:
+ cPluginLua * Plugin = GetLuaPlugin(L);
+ if (Plugin == nullptr)
+ {
+ // An error message has been already printed in GetLuaPlugin()
+ S.Push(false);
+ return 1;
+ }
+
+ // Read the params:
+ int Port;
+ S.GetStackValues(2, Port);
+ if ((Port < 0) || (Port > 65535))
+ {
+ LOGWARNING("cNetwork:Listen() called with invalid port (%d), failing the request.", Port);
+ S.Push(false);
+ return 1;
+ }
+ UInt16 Port16 = static_cast<UInt16>(Port);
+
+ // Create the LuaTCPLink glue class:
+ auto Srv = std::make_shared<cLuaServerHandle>(Port16, *Plugin, 3);
+
+ // Listen:
+ Srv->SetServerHandle(cNetwork::Listen(Port16, Srv), Srv);
+
+ // Register the server to be garbage-collected by Lua:
+ tolua_pushusertype(L, Srv.get(), "cServerHandle");
+ tolua_register_gc(L, lua_gettop(L));
+
+ // Return the server handle wrapper:
+ S.Push(Srv.get());
+ return 1;
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cTCPLink bindings (routed through cLuaTCPLink):
+
+/** Binds cLuaTCPLink::Send */
+static int tolua_cTCPLink_Send(lua_State * L)
+{
+ // Function signature:
+ // LinkInstance:Send(DataString)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cTCPLink") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the link:
+ cLuaTCPLink * Link;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cTCPLink:Send(): invalid link object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Link = *static_cast<cLuaTCPLink **>(lua_touserdata(L, 1));
+
+ // Get the data to send:
+ AString Data;
+ S.GetStackValues(2, Data);
+
+ // Send the data:
+ Link->Send(Data);
+ return 0;
+}
+
+
+
+
+
+/** Binds cLuaTCPLink::GetLocalIP */
+static int tolua_cTCPLink_GetLocalIP(lua_State * L)
+{
+ // Function signature:
+ // LinkInstance:GetLocalIP() -> string
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cTCPLink") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the link:
+ cLuaTCPLink * Link;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cTCPLink:GetLocalIP(): invalid link object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Link = *static_cast<cLuaTCPLink **>(lua_touserdata(L, 1));
+
+ // Get the IP:
+ S.Push(Link->GetLocalIP());
+ return 1;
+}
+
+
+
+
+
+/** Binds cLuaTCPLink::GetLocalPort */
+static int tolua_cTCPLink_GetLocalPort(lua_State * L)
+{
+ // Function signature:
+ // LinkInstance:GetLocalPort() -> number
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cTCPLink") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the link:
+ cLuaTCPLink * Link;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cTCPLink:GetLocalPort(): invalid link object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Link = *static_cast<cLuaTCPLink **>(lua_touserdata(L, 1));
+
+ // Get the Port:
+ S.Push(Link->GetLocalPort());
+ return 1;
+}
+
+
+
+
+
+/** Binds cLuaTCPLink::GetRemoteIP */
+static int tolua_cTCPLink_GetRemoteIP(lua_State * L)
+{
+ // Function signature:
+ // LinkInstance:GetRemoteIP() -> string
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cTCPLink") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the link:
+ cLuaTCPLink * Link;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cTCPLink:GetRemoteIP(): invalid link object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Link = *static_cast<cLuaTCPLink **>(lua_touserdata(L, 1));
+
+ // Get the IP:
+ S.Push(Link->GetRemoteIP());
+ return 1;
+}
+
+
+
+
+
+/** Binds cLuaTCPLink::GetRemotePort */
+static int tolua_cTCPLink_GetRemotePort(lua_State * L)
+{
+ // Function signature:
+ // LinkInstance:GetRemotePort() -> number
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cTCPLink") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the link:
+ cLuaTCPLink * Link;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cTCPLink:GetRemotePort(): invalid link object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Link = *static_cast<cLuaTCPLink **>(lua_touserdata(L, 1));
+
+ // Get the Port:
+ S.Push(Link->GetRemotePort());
+ return 1;
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cServerHandle bindings (routed through cLuaServerHandle):
+
+/** Called when Lua destroys the object instance.
+Close the server and let it deallocate on its own (it's in a SharedPtr). */
+static int tolua_collect_cServerHandle(lua_State * L)
+{
+ cLuaServerHandle * Srv = static_cast<cLuaServerHandle *>(tolua_tousertype(L, 1, nullptr));
+ Srv->Release();
+ return 0;
+}
+
+
+
+
+
+/** Binds cLuaServerHandle::Close */
+static int tolua_cServerHandle_Close(lua_State * L)
+{
+ // Function signature:
+ // ServerInstance:Close()
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cServerHandle") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the server handle:
+ cLuaServerHandle * Srv;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cServerHandle:Close(): invalid server handle object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Srv = *static_cast<cLuaServerHandle **>(lua_touserdata(L, 1));
+
+ // Close it:
+ Srv->Close();
+ return 0;
+}
+
+
+
+
+
+/** Binds cLuaServerHandle::IsListening */
+static int tolua_cServerHandle_IsListening(lua_State * L)
+{
+ // Function signature:
+ // ServerInstance:IsListening() -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cServerHandle") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the server handle:
+ cLuaServerHandle * Srv;
+ if (lua_isnil(L, 1))
+ {
+ LOGWARNING("cServerHandle:IsListening(): invalid server handle object. Stack trace:");
+ S.LogStackTrace();
+ return 0;
+ }
+ Srv = *static_cast<cLuaServerHandle **>(lua_touserdata(L, 1));
+
+ // Close it:
+ S.Push(Srv->IsListening());
+ return 1;
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Register the bindings:
+
+void ManualBindings::BindNetwork(lua_State * tolua_S)
+{
+ // Create the cNetwork API classes:
+ tolua_usertype(tolua_S, "cNetwork");
+ tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr);
+ tolua_usertype(tolua_S, "cTCPLink");
+ tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr);
+ tolua_usertype(tolua_S, "cServerHandle");
+ tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle);
+
+ // Fill in the functions (alpha-sorted):
+ tolua_beginmodule(tolua_S, "cNetwork");
+ tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect);
+ tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP);
+ tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname);
+ tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cTCPLink");
+ tolua_function(tolua_S, "Send", tolua_cTCPLink_Send);
+ tolua_function(tolua_S, "GetLocalIP", tolua_cTCPLink_GetLocalIP);
+ tolua_function(tolua_S, "GetLocalPort", tolua_cTCPLink_GetLocalPort);
+ tolua_function(tolua_S, "GetRemoteIP", tolua_cTCPLink_GetRemoteIP);
+ tolua_function(tolua_S, "GetRemotePort", tolua_cTCPLink_GetRemotePort);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cServerHandle");
+ tolua_function(tolua_S, "Close", tolua_cServerHandle_Close);
+ tolua_function(tolua_S, "IsListening", tolua_cServerHandle_IsListening);
+ tolua_endmodule(tolua_S);
+}
+
+
+
+
diff --git a/src/Globals.h b/src/Globals.h
index 654ede95f..29eaac871 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -381,6 +381,7 @@ void inline LOG(const char * a_Format, ...)
// Unified shared ptr from before C++11. Also no silly undercores.
#define SharedPtr std::shared_ptr
+#define WeakPtr std::weak_ptr
diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h
index cdf6ba0e9..e883dfb29 100644
--- a/src/OSSupport/Network.h
+++ b/src/OSSupport/Network.h
@@ -90,6 +90,9 @@ public:
Sends the RST packet, queued outgoing and incoming data is lost. */
virtual void Close(void) = 0;
+ /** Returns the callbacks that are used. */
+ cCallbacksPtr GetCallbacks(void) const { return m_Callbacks; }
+
protected:
/** Callbacks to be used for the various situations. */
cCallbacksPtr m_Callbacks;
diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp
index f97db7582..88fb57838 100644
--- a/src/OSSupport/TCPLinkImpl.cpp
+++ b/src/OSSupport/TCPLinkImpl.cpp
@@ -221,6 +221,8 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void
// Pending connection succeeded, call the connection callback:
if (a_What & BEV_EVENT_CONNECTED)
{
+ Self->UpdateLocalAddress();
+ Self->UpdateRemoteAddress();
if (Self->m_ConnectCallbacks != nullptr)
{
Self->m_ConnectCallbacks->OnConnected(*Self);
@@ -228,8 +230,6 @@ void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void
Self->m_ConnectCallbacks.reset();
return;
}
- Self->UpdateLocalAddress();
- Self->UpdateRemoteAddress();
}
// If the connection has been closed, call the link callback and remove the connection: