From e2bb051bd352bfae50f0ddcbcef844e62fa146fc Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Fri, 4 Nov 2016 21:50:46 +0100
Subject: degesch: replace degesch.connect with async.dial
Halfway there, looks much saner.
---
degesch.c | 275 +++++++++++++++++++++-----------------------
plugins/degesch/last-fm.lua | 19 ++-
2 files changed, 139 insertions(+), 155 deletions(-)
diff --git a/degesch.c b/degesch.c
index a761dee..641629c 100644
--- a/degesch.c
+++ b/degesch.c
@@ -9580,149 +9580,6 @@ lua_plugin_push_connection (struct lua_plugin *plugin, int socket_fd)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-/// Identifier for the Lua metatable
-#define XLUA_CONNECTOR_METATABLE "connector"
-
-struct lua_connector
-{
- struct lua_plugin *plugin; ///< The plugin we belong to
- struct connector connector; ///< Connector object
- bool active; ///< Whether the connector is alive
-
- int ref_on_success; ///< Reference to "on_success" callback
- int ref_on_error; ///< Reference to "on_error" callback
-
- char *last_error; ///< Connecting error, if any
-};
-
-static void
-lua_connector_discard (struct lua_connector *self)
-{
- if (self->active)
- {
- connector_free (&self->connector);
- self->active = false;
-
- luaL_unref (self->plugin->L, LUA_REGISTRYINDEX, self->ref_on_success);
- luaL_unref (self->plugin->L, LUA_REGISTRYINDEX, self->ref_on_error);
- self->ref_on_success = LUA_REFNIL;
- self->ref_on_error = LUA_REFNIL;
- }
-
- free (self->last_error);
- self->last_error = NULL;
-
- lua_cache_invalidate (self->plugin->L, self);
-}
-
-static int
-lua_connector_abort (lua_State *L)
-{
- lua_connector_discard (luaL_checkudata (L, 1, XLUA_CONNECTOR_METATABLE));
- return 0;
-}
-
-static luaL_Reg lua_connector_table[] =
-{
- { "abort", lua_connector_abort },
- { "__gc", lua_connector_abort },
- { NULL, NULL }
-};
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-static void
-lua_connector_on_connected (void *user_data, int socket, const char *hostname)
-{
- struct lua_connector *self = user_data;
-
- // TODO: use the hostname for SNI once TLS is implemented
-
- if (self->ref_on_success != LUA_REFNIL)
- {
- lua_State *L = self->plugin->L;
- lua_rawgeti (L, LUA_REGISTRYINDEX, self->ref_on_success);
- struct lua_connection *connection =
- lua_plugin_push_connection (self->plugin, socket); // 1: connection
- lua_pushstring (L, hostname); // 2: hostname
-
- struct error *e = NULL;
- if (!lua_plugin_call (self->plugin, 2, 0, &e))
- {
- lua_plugin_log_error (self->plugin, "connector on_success", e);
- // The connection has placed itself in the cache
- lua_connection_discard (connection);
- }
- }
-
- lua_connector_discard (self);
-}
-
-static void
-lua_connector_on_failure (void *user_data)
-{
- struct lua_connector *self = user_data;
- if (self->ref_on_error != LUA_REFNIL)
- {
- lua_State *L = self->plugin->L;
- lua_rawgeti (L, LUA_REGISTRYINDEX, self->ref_on_error);
- lua_pushstring (L, self->last_error); // 1: error string
-
- struct error *e = NULL;
- if (!lua_plugin_call (self->plugin, 1, 0, &e))
- lua_plugin_log_error (self->plugin, "connector on_error", e);
- }
-
- lua_connector_discard (self);
-}
-
-static void
-lua_connector_on_error (void *user_data, const char *error)
-{
- struct lua_connector *self = user_data;
- free (self->last_error);
- self->last_error = xstrdup (error);
-}
-
-static int
-lua_plugin_connect (lua_State *L)
-{
- struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
- const char *host = luaL_checkstring (L, 1);
- const char *service = luaL_checkstring (L, 2);
- luaL_checktype (L, 3, LUA_TTABLE);
-
- struct lua_connector *self = lua_newuserdata (L, sizeof *self);
- luaL_setmetatable (L, XLUA_CONNECTOR_METATABLE);
- memset (self, 0, sizeof *self);
-
- self->plugin = plugin;
- self->ref_on_success = LUA_REFNIL;
- self->ref_on_error = LUA_REFNIL;
-
- (void) lua_plugin_check_field (L, 3, "on_success", LUA_TFUNCTION, true);
- self->ref_on_success = luaL_ref (L, LUA_REGISTRYINDEX);
- (void) lua_plugin_check_field (L, 3, "on_error", LUA_TFUNCTION, true);
- self->ref_on_error = luaL_ref (L, LUA_REGISTRYINDEX);
-
- struct app_context *ctx = plugin->ctx;
- struct connector *connector = &self->connector;
- connector_init (connector, &ctx->poller);
- connector_add_target (connector, host, service);
-
- connector->on_connected = lua_connector_on_connected;
- connector->on_connecting = NULL;
- connector->on_error = lua_connector_on_error;
- connector->on_failure = lua_connector_on_failure;
- connector->user_data = self;
-
- self->active = true;
- lua_cache_store (L, self, -1);
- return 1;
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
// The script can create as many wait channels as wanted. They only actually
// do anything once they get yielded to the main lua_resume() call.
@@ -9978,6 +9835,127 @@ lua_plugin_push_wait_timer (struct lua_plugin *plugin, lua_State *L,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+struct lua_wait_dial
+{
+ struct lua_wait_channel super; ///< The structure we're deriving
+
+ struct lua_plugin *plugin; ///< The plugin we belong to
+ struct connector connector; ///< Connector object
+ bool active; ///< Whether the connector is alive
+
+ struct lua_connection *connection; ///< Established connection
+ char *hostname; ///< Target hostname
+ char *last_error; ///< Connecting error, if any
+};
+
+static bool
+lua_wait_dial_check (struct lua_wait_channel *wchannel)
+{
+ struct lua_wait_dial *self =
+ CONTAINER_OF (wchannel, struct lua_wait_dial, super);
+
+ lua_State *L = self->super.task->thread;
+ if (self->connection)
+ {
+ // FIXME: this way the connection can leak, it shouldn't stay in cache
+ // automatically all the time but clean itself up on GC
+ lua_cache_get (L, self->connection);
+ lua_pushstring (L, self->hostname);
+ self->connection = NULL;
+ }
+ else if (self->last_error)
+ {
+ lua_pushnil (L);
+ lua_pushnil (L);
+ lua_pushstring (L, self->last_error);
+ }
+ else
+ return false;
+ return true;
+}
+
+static void
+lua_wait_dial_cancel (struct lua_wait_dial *self)
+{
+ if (self->active)
+ {
+ connector_free (&self->connector);
+ self->active = false;
+ }
+}
+
+static void
+lua_wait_dial_cleanup (struct lua_wait_channel *wchannel)
+{
+ struct lua_wait_dial *self =
+ CONTAINER_OF (wchannel, struct lua_wait_dial, super);
+
+ lua_wait_dial_cancel (self);
+ if (self->connection)
+ lua_connection_discard (self->connection);
+
+ free (self->hostname);
+ free (self->last_error);
+}
+
+static void
+lua_wait_dial_on_connected (void *user_data, int socket, const char *hostname)
+{
+ struct lua_wait_dial *self = user_data;
+ if (self->super.task)
+ lua_task_wakeup (self->super.task);
+
+ self->connection = lua_plugin_push_connection (self->plugin, socket);
+ // TODO: use the hostname for SNI once TLS is implemented
+ self->hostname = xstrdup (hostname);
+ lua_wait_dial_cancel (self);
+}
+
+static void
+lua_wait_dial_on_failure (void *user_data)
+{
+ struct lua_wait_dial *self = user_data;
+ if (self->super.task)
+ lua_task_wakeup (self->super.task);
+ lua_wait_dial_cancel (self);
+}
+
+static void
+lua_wait_dial_on_error (void *user_data, const char *error)
+{
+ struct lua_wait_dial *self = user_data;
+ free (self->last_error);
+ self->last_error = xstrdup (error);
+}
+
+static int
+lua_plugin_push_wait_dial (struct lua_plugin *plugin, lua_State *L,
+ const char *host, const char *service)
+{
+ struct lua_wait_dial *self = lua_newuserdata (L, sizeof *self);
+ luaL_setmetatable (L, XLUA_WCHANNEL_METATABLE);
+ memset (self, 0, sizeof *self);
+
+ self->super.check = lua_wait_dial_check;
+ self->super.cleanup = lua_wait_dial_cleanup;
+
+ struct connector *connector = &self->connector;
+ connector_init (connector, &plugin->ctx->poller);
+ connector_add_target (connector, host, service);
+
+ connector->on_connected = lua_wait_dial_on_connected;
+ connector->on_connecting = NULL;
+ connector->on_error = lua_wait_dial_on_error;
+ connector->on_failure = lua_wait_dial_on_failure;
+ connector->user_data = self;
+
+ self->plugin = plugin;
+ self->active = true;
+ return 1;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
static int
lua_async_go (lua_State *L)
{
@@ -10017,10 +9995,19 @@ lua_async_timer_ms (lua_State *L)
return lua_plugin_push_wait_timer (plugin, L, timeout);
}
+static int
+lua_async_dial (lua_State *L)
+{
+ struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
+ return lua_plugin_push_wait_dial (plugin, L,
+ luaL_checkstring (L, 1), luaL_checkstring (L, 2));
+}
+
static luaL_Reg lua_async_library[] =
{
{ "go", lua_async_go },
{ "timer_ms", lua_async_timer_ms },
+ { "dial", lua_async_dial },
{ NULL, NULL },
};
@@ -10050,7 +10037,6 @@ static luaL_Reg lua_plugin_library[] =
{ "hook_prompt", lua_plugin_hook_prompt },
{ "hook_completion", lua_plugin_hook_completion },
{ "setup_config", lua_plugin_setup_config },
- { "connect", lua_plugin_connect },
// And these are methods:
@@ -10332,7 +10318,6 @@ lua_plugin_load (struct app_context *ctx, const char *filename,
lua_plugin_reg_weak (L, &lua_server_info, lua_server_table);
lua_plugin_reg_meta (L, XLUA_SCHEMA_METATABLE, lua_schema_table);
lua_plugin_reg_meta (L, XLUA_CONNECTION_METATABLE, lua_connection_table);
- lua_plugin_reg_meta (L, XLUA_CONNECTOR_METATABLE, lua_connector_table);
lua_plugin_reg_meta (L, XLUA_TASK_METATABLE, lua_task_table);
lua_plugin_reg_meta (L, XLUA_WCHANNEL_METATABLE, lua_wchannel_table);
diff --git a/plugins/degesch/last-fm.lua b/plugins/degesch/last-fm.lua
index 5c836ca..8f6902d 100644
--- a/plugins/degesch/last-fm.lua
+++ b/plugins/degesch/last-fm.lua
@@ -118,24 +118,23 @@ end
local running
-- Initiate a connection to last.fm servers
+async, await = degesch.async, coroutine.yield
local make_request = function (buffer, action)
if not user or not api_key then
report_error (buffer, "configuration is incomplete")
return
end
- if running then running.abort () end
-
- running = degesch.connect ("ws.audioscrobbler.com", 80, {
- on_success = function (c, host)
- on_connected (buffer, c, host, action)
- running = nil
- end,
- on_error = function (e)
+ if running then running:cancel () end
+ running = async.go (function ()
+ local c, host, e = await (async.dial ("ws.audioscrobbler.com", 80))
+ if e then
report_error (buffer, e)
- running = nil
+ else
+ on_connected (buffer, c, host, action)
end
- })
+ running = nil
+ end)
end
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
cgit v1.2.3-70-g09d2