MINOR: lua: add AppletTCP class and service
This class is used by Lua code for running as an applet called in TCP mode.
It defines also the lua service.
diff --git a/include/types/applet.h b/include/types/applet.h
index 21978bf..2254b0a 100644
--- a/include/types/applet.h
+++ b/include/types/applet.h
@@ -116,6 +116,11 @@
struct list wake_on_write;
} hlua;
struct {
+ struct hlua hlua;
+ int flags;
+ struct task *task;
+ } hlua_apptcp;
+ struct {
struct dns_resolvers *ptr;
} resolvers;
struct {
diff --git a/include/types/hlua.h b/include/types/hlua.h
index 1bf2a75..eccfc6c 100644
--- a/include/types/hlua.h
+++ b/include/types/hlua.h
@@ -17,6 +17,7 @@
#define CLASS_CHANNEL "Channel"
#define CLASS_HTTP "HTTP"
#define CLASS_MAP "Map"
+#define CLASS_APPLET_TCP "AppletTCP"
struct stream;
@@ -98,6 +99,13 @@
struct proxy *p;
};
+/* This struct contains the applet context. */
+struct hlua_appctx {
+ struct appctx *appctx;
+ luaL_Buffer b; /* buffer used to prepare strings. */
+ struct hlua_txn htxn;
+};
+
/* This struc is used with sample fetches and sample converters. */
struct hlua_smp {
struct stream *s;
diff --git a/src/hlua.c b/src/hlua.c
index 2682129..36bd100 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -109,6 +109,10 @@
lua_atpanic(__L, hlua_panic_safe); \
} while(0)
+/* Applet status flags */
+#define APPLET_DONE 0x1 /* applet processing is done. */
+#define APPLET_SENT 0x2 /* some data are sent. */
+
/* The main Lua execution context. */
struct hlua gL;
@@ -138,6 +142,7 @@
static int class_converters_ref;
static int class_http_ref;
static int class_map_ref;
+static int class_applet_tcp_ref;
/* Global Lua execution timeout. By default Lua, execution linked
* with stream (actions, sample-fetches and converters) have a
@@ -146,6 +151,7 @@
*/
static unsigned int hlua_timeout_session = 4000; /* session timeout. */
static unsigned int hlua_timeout_task = TICK_ETERNITY; /* task timeout. */
+static unsigned int hlua_timeout_applet = 4000; /* applet timeout. */
/* Interrupts the Lua processing each "hlua_nb_instruction" instructions.
* it is used for preventing infinite loops.
@@ -3134,6 +3140,287 @@
return 1;
}
+/*
+ *
+ *
+ * Class AppletTCP
+ *
+ *
+ */
+
+/* Returns a struct hlua_txn if the stack entry "ud" is
+ * a class stream, otherwise it throws an error.
+ */
+__LJMP static struct hlua_appctx *hlua_checkapplet_tcp(lua_State *L, int ud)
+{
+ return (struct hlua_appctx *)MAY_LJMP(hlua_checkudata(L, ud, class_applet_tcp_ref));
+}
+
+/* This function creates and push in the stack an Applet object
+ * according with a current TXN.
+ */
+static int hlua_applet_tcp_new(lua_State *L, struct appctx *ctx)
+{
+ struct hlua_appctx *appctx;
+ struct stream_interface *si = ctx->owner;
+ struct stream *s = si_strm(si);
+ struct proxy *p = s->be;
+
+ /* Check stack size. */
+ if (!lua_checkstack(L, 3))
+ return 0;
+
+ /* Create the object: obj[0] = userdata.
+ * Note that the base of the Converters object is the
+ * same than the TXN object.
+ */
+ lua_newtable(L);
+ appctx = lua_newuserdata(L, sizeof(*appctx));
+ lua_rawseti(L, -2, 0);
+ appctx->appctx = ctx;
+ appctx->htxn.s = s;
+ appctx->htxn.p = p;
+
+ /* Create the "f" field that contains a list of fetches. */
+ lua_pushstring(L, "f");
+ if (!hlua_fetches_new(L, &appctx->htxn, 0))
+ return 0;
+ lua_settable(L, -3);
+
+ /* Create the "sf" field that contains a list of stringsafe fetches. */
+ lua_pushstring(L, "sf");
+ if (!hlua_fetches_new(L, &appctx->htxn, 1))
+ return 0;
+ lua_settable(L, -3);
+
+ /* Create the "c" field that contains a list of converters. */
+ lua_pushstring(L, "c");
+ if (!hlua_converters_new(L, &appctx->htxn, 0))
+ return 0;
+ lua_settable(L, -3);
+
+ /* Create the "sc" field that contains a list of stringsafe converters. */
+ lua_pushstring(L, "sc");
+ if (!hlua_converters_new(L, &appctx->htxn, 1))
+ return 0;
+ lua_settable(L, -3);
+
+ /* Pop a class stream metatable and affect it to the table. */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, class_applet_tcp_ref);
+ lua_setmetatable(L, -2);
+
+ return 1;
+}
+
+/* If expected data not yet available, it returns a yield. This function
+ * consumes the data in the buffer. It returns a string containing the
+ * data. This string can be empty.
+ */
+__LJMP static int hlua_applet_tcp_getline_yield(lua_State *L, int status, lua_KContext ctx)
+{
+ struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
+ struct stream_interface *si = appctx->appctx->owner;
+ int ret;
+ char *blk1;
+ int len1;
+ char *blk2;
+ int len2;
+
+ /* Read the maximum amount of data avalaible. */
+ ret = bo_getline_nc(si_oc(si), &blk1, &len1, &blk2, &len2);
+
+ /* Data not yet avalaible. return yield. */
+ if (ret == 0) {
+ si_applet_cant_get(si);
+ WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_getline_yield, TICK_ETERNITY, 0));
+ }
+
+ /* End of data: commit the total strings and return. */
+ if (ret < 0) {
+ luaL_pushresult(&appctx->b);
+ return 1;
+ }
+
+ /* Ensure that the block 2 length is usable. */
+ if (ret == 1)
+ len2 = 0;
+
+ /* dont check the max length read and dont check. */
+ luaL_addlstring(&appctx->b, blk1, len1);
+ luaL_addlstring(&appctx->b, blk2, len2);
+
+ /* Consume input channel output buffer data. */
+ bo_skip(si_oc(si), len1 + len2);
+ luaL_pushresult(&appctx->b);
+ return 1;
+}
+
+/* Check arguments for the fucntion "hlua_channel_get_yield". */
+__LJMP static int hlua_applet_tcp_getline(lua_State *L)
+{
+ struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
+
+ /* Initialise the string catenation. */
+ luaL_buffinit(L, &appctx->b);
+
+ return MAY_LJMP(hlua_applet_tcp_getline_yield(L, 0, 0));
+}
+
+/* If expected data not yet available, it returns a yield. This function
+ * consumes the data in the buffer. It returns a string containing the
+ * data. This string can be empty.
+ */
+__LJMP static int hlua_applet_tcp_recv_yield(lua_State *L, int status, lua_KContext ctx)
+{
+ struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
+ struct stream_interface *si = appctx->appctx->owner;
+ int len = MAY_LJMP(luaL_checkinteger(L, 2));
+ int ret;
+ char *blk1;
+ int len1;
+ char *blk2;
+ int len2;
+
+ /* Read the maximum amount of data avalaible. */
+ ret = bo_getblk_nc(si_oc(si), &blk1, &len1, &blk2, &len2);
+
+ /* Data not yet avalaible. return yield. */
+ if (ret == 0) {
+ si_applet_cant_get(si);
+ WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_recv_yield, TICK_ETERNITY, 0));
+ }
+
+ /* End of data: commit the total strings and return. */
+ if (ret < 0) {
+ luaL_pushresult(&appctx->b);
+ return 1;
+ }
+
+ /* Ensure that the block 2 length is usable. */
+ if (ret == 1)
+ len2 = 0;
+
+ if (len == -1) {
+
+ /* If len == -1, catenate all the data avalaile and
+ * yield because we want to get all the data until
+ * the end of data stream.
+ */
+ luaL_addlstring(&appctx->b, blk1, len1);
+ luaL_addlstring(&appctx->b, blk2, len2);
+ bo_skip(si_oc(si), len1 + len2);
+ si_applet_cant_get(si);
+ WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_recv_yield, TICK_ETERNITY, 0));
+
+ } else {
+
+ /* Copy the fisrt block caping to the length required. */
+ if (len1 > len)
+ len1 = len;
+ luaL_addlstring(&appctx->b, blk1, len1);
+ len -= len1;
+
+ /* Copy the second block. */
+ if (len2 > len)
+ len2 = len;
+ luaL_addlstring(&appctx->b, blk2, len2);
+ len -= len2;
+
+ /* Consume input channel output buffer data. */
+ bo_skip(si_oc(si), len1 + len2);
+
+ /* If we are no other data avalaible, yield waiting for new data. */
+ if (len > 0) {
+ lua_pushinteger(L, len);
+ lua_replace(L, 2);
+ si_applet_cant_get(si);
+ WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_recv_yield, TICK_ETERNITY, 0));
+ }
+
+ /* return the result. */
+ luaL_pushresult(&appctx->b);
+ return 1;
+ }
+
+ /* we never executes this */
+ hlua_pusherror(L, "Lua: internal error");
+ WILL_LJMP(lua_error(L));
+ return 0;
+}
+
+/* Check arguments for the fucntion "hlua_channel_get_yield". */
+__LJMP static int hlua_applet_tcp_recv(lua_State *L)
+{
+ struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
+ int len = -1;
+
+ if (lua_gettop(L) > 2)
+ WILL_LJMP(luaL_error(L, "The 'recv' function requires between 1 and 2 arguments."));
+ if (lua_gettop(L) >= 2) {
+ len = MAY_LJMP(luaL_checkinteger(L, 2));
+ lua_pop(L, 1);
+ }
+
+ /* Confirm or set the required length */
+ lua_pushinteger(L, len);
+
+ /* Initialise the string catenation. */
+ luaL_buffinit(L, &appctx->b);
+
+ return MAY_LJMP(hlua_applet_tcp_recv_yield(L, 0, 0));
+}
+
+/* Append data in the output side of the buffer. This data is immediatly
+ * sent. The fcuntion returns the ammount of data writed. If the buffer
+ * cannot contains the data, the function yield. The function returns -1
+ * if the channel is closed.
+ */
+__LJMP static int hlua_applet_tcp_send_yield(lua_State *L, int status, lua_KContext ctx)
+{
+ size_t len;
+ struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
+ const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
+ int l = MAY_LJMP(luaL_checkinteger(L, 3));
+ struct stream_interface *si = appctx->appctx->owner;
+ struct channel *chn = si_ic(si);
+ int max;
+
+ /* Get the max amount of data which can write as input in the channel. */
+ max = channel_recv_max(chn);
+ if (max > (len - l))
+ max = len - l;
+
+ /* Copy data. */
+ bi_putblk(chn, str + l, max);
+
+ /* update counters. */
+ l += max;
+ lua_pop(L, 1);
+ lua_pushinteger(L, l);
+
+ /* If some data is not send, declares the situation to the
+ * applet, and returns a yield.
+ */
+ if (l < len) {
+ si_applet_cant_put(si);
+ WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_send_yield, TICK_ETERNITY, 0));
+ }
+
+ return 1;
+}
+
+/* Just a wraper of "hlua_applet_tcp_send_yield". This wrapper permits
+ * yield the LUA process, and resume it without checking the
+ * input arguments.
+ */
+__LJMP static int hlua_applet_tcp_send(lua_State *L)
+{
+ MAY_LJMP(check_args(L, 2, "send"));
+ lua_pushinteger(L, 0);
+
+ return MAY_LJMP(hlua_applet_tcp_send_yield(L, 0, 0));
+}
+
/*
*
*
@@ -4624,6 +4911,176 @@
}
}
+struct task *hlua_applet_wakeup(struct task *t)
+{
+ struct appctx *ctx = t->context;
+ struct stream_interface *si = ctx->owner;
+
+ /* If the applet is wake up without any expected work, the sheduler
+ * remove it from the run queue. This flag indicate that the applet
+ * is waiting for write. If the buffer is full, the main processing
+ * will send some data and after call the applet, otherwise it call
+ * the applet ASAP.
+ */
+ si_applet_cant_put(si);
+ appctx_wakeup(ctx);
+ return NULL;
+}
+
+static int hlua_applet_tcp_init(struct appctx *ctx, struct proxy *px, struct stream *strm)
+{
+ struct stream_interface *si = ctx->owner;
+ struct hlua *hlua = &ctx->ctx.hlua_apptcp.hlua;
+ struct task *task;
+ char **arg;
+
+ HLUA_INIT(hlua);
+ ctx->ctx.hlua_apptcp.flags = 0;
+
+ /* Create task used by signal to wakeup applets. */
+ task = task_new();
+ if (!task) {
+ SEND_ERR(px, "Lua applet tcp '%s': out of memory.\n",
+ ctx->rule->arg.hlua_rule->fcn.name);
+ return 0;
+ }
+ task->nice = 0;
+ task->context = ctx;
+ task->process = hlua_applet_wakeup;
+ ctx->ctx.hlua_apptcp.task = task;
+
+ /* In the execution wrappers linked with a stream, the
+ * Lua context can be not initialized. This behavior
+ * permits to save performances because a systematic
+ * Lua initialization cause 5% performances loss.
+ */
+ if (!hlua_ctx_init(hlua, task)) {
+ SEND_ERR(px, "Lua applet tcp '%s': can't initialize Lua context.\n",
+ ctx->rule->arg.hlua_rule->fcn.name);
+ return 0;
+ }
+
+ /* Set timeout according with the applet configuration. */
+ hlua->expire = tick_add_ifset(now_ms, ctx->applet->timeout);
+
+ /* The following Lua calls can fail. */
+ if (!SET_SAFE_LJMP(hlua->T)) {
+ SEND_ERR(px, "Lua applet tcp '%s': critical error.\n",
+ ctx->rule->arg.hlua_rule->fcn.name);
+ RESET_SAFE_LJMP(hlua->T);
+ return 0;
+ }
+
+ /* Check stack available size. */
+ if (!lua_checkstack(hlua->T, 1)) {
+ SEND_ERR(px, "Lua applet tcp '%s': full stack.\n",
+ ctx->rule->arg.hlua_rule->fcn.name);
+ RESET_SAFE_LJMP(hlua->T);
+ return 0;
+ }
+
+ /* Restore the function in the stack. */
+ lua_rawgeti(hlua->T, LUA_REGISTRYINDEX, ctx->rule->arg.hlua_rule->fcn.function_ref);
+
+ /* Create and and push object stream in the stack. */
+ if (!hlua_applet_tcp_new(hlua->T, ctx)) {
+ SEND_ERR(px, "Lua applet tcp '%s': full stack.\n",
+ ctx->rule->arg.hlua_rule->fcn.name);
+ RESET_SAFE_LJMP(hlua->T);
+ return 0;
+ }
+ hlua->nargs = 1;
+
+ /* push keywords in the stack. */
+ for (arg = ctx->rule->arg.hlua_rule->args; arg && *arg; arg++) {
+ if (!lua_checkstack(hlua->T, 1)) {
+ SEND_ERR(px, "Lua applet tcp '%s': full stack.\n",
+ ctx->rule->arg.hlua_rule->fcn.name);
+ RESET_SAFE_LJMP(hlua->T);
+ return 0;
+ }
+ lua_pushstring(hlua->T, *arg);
+ hlua->nargs++;
+ }
+
+ RESET_SAFE_LJMP(hlua->T);
+
+ /* Wakeup the applet ASAP. */
+ si_applet_cant_get(si);
+ si_applet_cant_put(si);
+
+ return 1;
+}
+
+static void hlua_applet_tcp_fct(struct appctx *ctx)
+{
+ struct stream_interface *si = ctx->owner;
+ struct stream *strm = si_strm(si);
+ struct channel *res = si_ic(si);
+ struct act_rule *rule = ctx->rule;
+ struct proxy *px = strm->be;
+ struct hlua *hlua = &ctx->ctx.hlua_apptcp.hlua;
+
+ /* The applet execution is already done. */
+ if (ctx->ctx.hlua_apptcp.flags & APPLET_DONE)
+ return;
+
+ /* If the stream is disconnect or closed, ldo nothing. */
+ if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
+ return;
+
+ /* Execute the function. */
+ switch (hlua_ctx_resume(hlua, 1)) {
+ /* finished. */
+ case HLUA_E_OK:
+ ctx->ctx.hlua_apptcp.flags |= APPLET_DONE;
+
+ /* log time */
+ strm->logs.tv_request = now;
+
+ /* eat the whole request */
+ bo_skip(si_oc(si), si_ob(si)->o);
+ res->flags |= CF_READ_NULL;
+ si_shutr(si);
+ return;
+
+ /* yield. */
+ case HLUA_E_AGAIN:
+ return;
+
+ /* finished with error. */
+ case HLUA_E_ERRMSG:
+ /* Display log. */
+ SEND_ERR(px, "Lua applet tcp '%s': %s.\n",
+ rule->arg.hlua_rule->fcn.name, lua_tostring(hlua->T, -1));
+ lua_pop(hlua->T, 1);
+ goto error;
+
+ case HLUA_E_ERR:
+ /* Display log. */
+ SEND_ERR(px, "Lua applet tcp '%s' return an unknown error.\n",
+ rule->arg.hlua_rule->fcn.name);
+ goto error;
+
+ default:
+ goto error;
+ }
+
+error:
+
+ /* For all other cases, just close the stream. */
+ si_shutw(si);
+ si_shutr(si);
+ ctx->ctx.hlua_apptcp.flags |= APPLET_DONE;
+}
+
+static void hlua_applet_tcp_release(struct appctx *ctx)
+{
+ task_free(ctx->ctx.hlua_apptcp.task);
+ ctx->ctx.hlua_apptcp.task = NULL;
+ hlua_ctx_destroy(&ctx->ctx.hlua_apptcp.hlua);
+}
+
/* global {tcp|http}-request parser. Return ACT_RET_PRS_OK in
* succes case, else return ACT_RET_PRS_ERR.
*
@@ -4731,7 +5188,104 @@
/* pop the environment string. */
lua_pop(L, 1);
}
+ return ACT_RET_PRS_OK;
+}
+
+static enum act_parse_ret action_register_service_tcp(const char **args, int *cur_arg, struct proxy *px,
+ struct act_rule *rule, char **err)
+{
+ struct hlua_function *fcn = (struct hlua_function *)rule->kw->private;
+
+ /* Memory for the rule. */
+ rule->arg.hlua_rule = calloc(1, sizeof(*rule->arg.hlua_rule));
+ if (!rule->arg.hlua_rule) {
+ memprintf(err, "out of memory error");
+ return ACT_RET_PRS_ERR;
+ }
+ /* Reference the Lua function and store the reference. */
+ rule->arg.hlua_rule->fcn = *fcn;
+
+ /* TODO: later accept arguments. */
+ rule->arg.hlua_rule->args = NULL;
+
+ /* Add applet pointer in the rule. */
+ rule->applet.obj_type = OBJ_TYPE_APPLET;
+ rule->applet.name = fcn->name;
+ rule->applet.init = hlua_applet_tcp_init;
+ rule->applet.fct = hlua_applet_tcp_fct;
+ rule->applet.release = hlua_applet_tcp_release;
+ rule->applet.timeout = hlua_timeout_applet;
+
+ return 0;
+}
+
+/* This function is an LUA binding used for registering
+ * "sample-conv" functions. It expects a converter name used
+ * in the haproxy configuration file, and an LUA function.
+ */
+__LJMP static int hlua_register_service(lua_State *L)
+{
+ struct action_kw_list *akl;
+ const char *name;
+ const char *env;
+ int ref;
+ int len;
+ struct hlua_function *fcn;
+
+ MAY_LJMP(check_args(L, 3, "register_service"));
+
+ /* First argument : converter name. */
+ name = MAY_LJMP(luaL_checkstring(L, 1));
+
+ /* Second argument : environment. */
+ env = MAY_LJMP(luaL_checkstring(L, 2));
+
+ /* Third argument : lua function. */
+ ref = MAY_LJMP(hlua_checkfunction(L, 3));
+
+ /* Check required environment. Only accepted "http" or "tcp". */
+ /* Allocate and fill the sample fetch keyword struct. */
+ akl = calloc(1, sizeof(*akl) + sizeof(struct action_kw) * 2);
+ if (!akl)
+ WILL_LJMP(luaL_error(L, "lua out of memory error."));
+ fcn = calloc(1, sizeof(*fcn));
+ if (!fcn)
+ WILL_LJMP(luaL_error(L, "lua out of memory error."));
+
+ /* Fill fcn. */
+ len = strlen("<lua.>") + strlen(name) + 1;
+ fcn->name = calloc(1, len);
+ if (!fcn->name)
+ WILL_LJMP(luaL_error(L, "lua out of memory error."));
+ snprintf((char *)fcn->name, len, "<lua.%s>", name);
+ fcn->function_ref = ref;
+
+ /* List head */
+ akl->list.n = akl->list.p = NULL;
+
+ /* converter keyword. */
+ len = strlen("lua.") + strlen(name) + 1;
+ akl->kw[0].kw = calloc(1, len);
+ if (!akl->kw[0].kw)
+ WILL_LJMP(luaL_error(L, "lua out of memory error."));
+
+ snprintf((char *)akl->kw[0].kw, len, "lua.%s", name);
+
+ if (strcmp(env, "tcp") == 0)
+ akl->kw[0].parse = action_register_service_tcp;
+ else
+ WILL_LJMP(luaL_error(L, "lua service environment '%s' is unknown. 'tcp' is expected."));
+
+ akl->kw[0].match_pfx = 0;
+ akl->kw[0].private = fcn;
+
+ /* End of array. */
+ memset(&akl->kw[1], 0, sizeof(*akl->kw));
+
+ /* Register this new converter */
+ service_keywords_register(akl);
+
return 0;
}
@@ -4765,6 +5319,14 @@
file, line, err, &hlua_timeout_task);
}
+static int hlua_applet_timeout(char **args, int section_type, struct proxy *curpx,
+ struct proxy *defpx, const char *file, int line,
+ char **err)
+{
+ return hlua_read_timeout(args, section_type, curpx, defpx,
+ file, line, err, &hlua_timeout_applet);
+}
+
static int hlua_forced_yield(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err)
@@ -4860,6 +5422,7 @@
{ CFG_GLOBAL, "lua-load", hlua_load },
{ CFG_GLOBAL, "tune.lua.session-timeout", hlua_session_timeout },
{ CFG_GLOBAL, "tune.lua.task-timeout", hlua_task_timeout },
+ { CFG_GLOBAL, "tune.lua.applet-timeout", hlua_applet_timeout },
{ CFG_GLOBAL, "tune.lua.forced-yield", hlua_forced_yield },
{ CFG_GLOBAL, "tune.lua.maxmem", hlua_parse_maxmem },
{ 0, NULL, NULL },
@@ -5005,6 +5568,7 @@
hlua_class_function(gL.T, "register_fetches", hlua_register_fetches);
hlua_class_function(gL.T, "register_converters", hlua_register_converters);
hlua_class_function(gL.T, "register_action", hlua_register_action);
+ hlua_class_function(gL.T, "register_service", hlua_register_service);
hlua_class_function(gL.T, "yield", hlua_yield);
hlua_class_function(gL.T, "set_nice", hlua_set_nice);
hlua_class_function(gL.T, "sleep", hlua_sleep);
@@ -5264,6 +5828,37 @@
/*
*
+ * Register class AppletTCP
+ *
+ */
+
+ /* Create and fill the metatable. */
+ lua_newtable(gL.T);
+
+ /* Create the __tostring identifier */
+ lua_pushstring(gL.T, "__tostring");
+ lua_pushstring(gL.T, CLASS_APPLET_TCP);
+ lua_pushcclosure(gL.T, hlua_dump_object, 1);
+ lua_rawset(gL.T, -3);
+
+ /* Create and fille the __index entry. */
+ lua_pushstring(gL.T, "__index");
+ lua_newtable(gL.T);
+
+ /* Register Lua functions. */
+ hlua_class_function(gL.T, "getline", hlua_applet_tcp_getline);
+ hlua_class_function(gL.T, "receive", hlua_applet_tcp_recv);
+ hlua_class_function(gL.T, "send", hlua_applet_tcp_send);
+
+ lua_settable(gL.T, -3);
+
+ /* Register previous table in the registry with reference and named entry. */
+ lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
+ lua_setfield(gL.T, LUA_REGISTRYINDEX, CLASS_APPLET_TCP); /* register class session. */
+ class_applet_tcp_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX); /* reference class session. */
+
+ /*
+ *
* Register class TXN
*
*/