MEDIUM: filters/lua: Add support for dummy filters written in lua

It is now possible to write dummy filters in lua. Only the basis to declare
such filters has been added for now. There is no way to declare callbacks to
filter anything. Lua filters are for now empty nutshells.

To do so, core.register_filter() must be called, with 3 arguments, the
filter's name (as it appears in HAProxy config), the lua class that will be
used to instantiate filters and a function to parse arguments passed on the
filter line in HAProxy configuration file. The lua filter class must at
least define the method new(), without any extra args, to create new
instances when streams are created. If this method is not found, the filter
will be ignored.

Here is a template to declare a new Lua filter:

    // haproxy.conf
    global
        lua-load /path/to/my-filter.lua
        ...

    frontend fe
        ...
        filter lua.my-lua-filter arg1 arg2 arg3
        filter lua.my-lua-filter arg4 arg5

    // my-filter.lua
    MyFilter = {}
    MyFilter.id = "My Lua filter"          -- the filter ID (optional)
    MyFilter.flags = filter.FLT_CFG_FL_HTX -- process HTX streams (optional)
    MyFilter.__index = MyFilter

    function MyFilter:new()
        flt = {}
        setmetatable(flt, MyFilter)
        -- Set any flt fields. self.args can be used
	flt.args = self.args

        return flt -- The new instance of Myfilter
    end

    core.register_filter("my-lua-filter", MyFilter, function(filter, args)
        -- process <args>, an array of strings. For instance:
        filter.args = args
        return filter
    end)

In this example, 2 filters are declared using the same lua class. The
parsing function is called for both, with its own copy of the lua class. So
each filter will be unique.

The global object "filter" exposes some constants and flags, and later some
functions, to help writting filters in lua.

Internally, when a lua filter is instantiated (so when new() method is
called), 2 lua contexts are created, one for the request channel and another
for the response channel. It is a prerequisite to let some callbacks yield
on one side independently on the other one.

There is no documentation for now.
diff --git a/src/hlua.c b/src/hlua.c
index 5c2d3cc..a7ad979 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -33,6 +33,7 @@
 #include <haproxy/channel.h>
 #include <haproxy/cli.h>
 #include <haproxy/connection.h>
+#include <haproxy/filters.h>
 #include <haproxy/h1.h>
 #include <haproxy/hlua.h>
 #include <haproxy/hlua_fcn.h>
@@ -195,6 +196,35 @@
  */
 static lua_State *hlua_states[MAX_THREADS + 1];
 
+struct hlua_reg_filter  {
+	char *name;
+	int flt_ref[MAX_THREADS + 1];
+	int fun_ref[MAX_THREADS + 1];
+	struct list l;
+};
+
+struct hlua_flt_config {
+	struct hlua_reg_filter *reg;
+	int ref[MAX_THREADS + 1];
+	char **args;
+};
+
+struct hlua_flt_ctx {
+	int ref;                 /* ref to the filter lua object */
+	struct hlua *hlua[2];    /* lua runtime context (0: request, 1: response) */
+	unsigned int cur_off[2]; /* current offset (0: request, 1: response) */
+	unsigned int cur_len[2]; /* current forwardable length (0: request, 1: response) */
+	unsigned int flags;      /* HLUA_FLT_CTX_FL_* */
+};
+
+DECLARE_STATIC_POOL(pool_head_hlua_flt_ctx, "hlua_flt_ctx", sizeof(struct hlua_flt_ctx));
+
+/* This is the chained list of struct hlua_flt referenced
+ * for haproxy filters. It is used for a post-initialisation control.
+ */
+static struct list referenced_filters = LIST_HEAD_INIT(referenced_filters);
+
+
 /* This is the memory pool containing struct lua for applets
  * (including cli).
  */
@@ -336,6 +366,47 @@
 	return 0;
 }
 
+/* Create a new registered filter. Only its name is filled */
+static inline struct hlua_reg_filter *new_hlua_reg_filter(const char *name)
+{
+	struct hlua_reg_filter *reg_flt;
+	int i;
+
+	reg_flt = calloc(1, sizeof(*reg_flt));
+	if (!reg_flt)
+		return NULL;
+	reg_flt->name = strdup(name);
+	if (!reg_flt->name) {
+		free(reg_flt);
+		return NULL;
+	}
+	LIST_APPEND(&referenced_filters, &reg_flt->l);
+	for (i = 0; i < MAX_THREADS + 1; i++) {
+		reg_flt->flt_ref[i] = -1;
+		reg_flt->fun_ref[i] = -1;
+	}
+	return reg_flt;
+}
+
+/* Release a registered filter */
+static inline void release_hlua_reg_filter(struct hlua_reg_filter *reg_flt)
+{
+	if (!reg_flt)
+		return;
+	if (reg_flt->name)
+		ha_free(&reg_flt->name);
+	LIST_DELETE(&reg_flt->l);
+	ha_free(&reg_flt);
+}
+
+/* If the common state is set, the stack id is 0, otherwise it is the tid + 1 */
+static inline int reg_flt_to_stack_id(struct hlua_reg_filter *reg_flt)
+{
+	if (reg_flt->fun_ref[0] == -1)
+		return tid + 1;
+	return 0;
+}
+
 /* Used to check an Lua function type in the stack. It creates and
  * returns a reference of the function. This function throws an
  * error if the rgument is not a "function".
@@ -8451,6 +8522,446 @@
 	return 0; /* Never reached */
 }
 
+static int hlua_filter_init_per_thread(struct proxy *px, struct flt_conf *fconf)
+{
+	struct hlua_flt_config *conf = fconf->conf;
+	lua_State *L;
+	int error, pos, state_id, flt_ref;
+
+	state_id = reg_flt_to_stack_id(conf->reg);
+	L = hlua_states[state_id];
+	pos = lua_gettop(L);
+
+	/* The filter parsing function */
+	lua_rawgeti(L, LUA_REGISTRYINDEX, conf->reg->fun_ref[state_id]);
+
+	/* Push the filter class on the stack and resolve all callbacks */
+	lua_rawgeti(L, LUA_REGISTRYINDEX, conf->reg->flt_ref[state_id]);
+
+	/* Duplicate the filter class so each filter will have its own copy */
+	lua_newtable(L);
+	lua_pushnil(L);
+
+        while (lua_next(L, pos+2)) {
+		lua_pushvalue(L, -2);
+		lua_insert(L, -2);
+		lua_settable(L, -4);
+	}
+	flt_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+	/* Remove the original lua filter class from the stack */
+	lua_pop(L, 1);
+
+	/* Push the copy on the stack */
+	lua_rawgeti(L, LUA_REGISTRYINDEX, flt_ref);
+
+	/* extra args are pushed in a table */
+	lua_newtable(L);
+	for (pos = 0; conf->args[pos]; pos++) {
+		/* Check stack available size. */
+		if (!lua_checkstack(L, 1)) {
+			ha_alert("Lua filter '%s' : Lua error : full stack.", conf->reg->name);
+			goto error;
+		}
+		lua_pushstring(L, conf->args[pos]);
+		lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
+	}
+
+	error = lua_pcall(L, 2, LUA_MULTRET, 0);
+	switch (error) {
+	case LUA_OK:
+		/* replace the filter ref */
+		conf->ref[state_id] = flt_ref;
+		break;
+	case LUA_ERRRUN:
+		ha_alert("Lua filter '%s' : runtime error : %s", conf->reg->name, lua_tostring(L, -1));
+		goto error;
+	case LUA_ERRMEM:
+		ha_alert("Lua filter '%s' : out of memory error", conf->reg->name);
+		goto error;
+	case LUA_ERRERR:
+		ha_alert("Lua filter '%s' : message handler error : %s", conf->reg->name, lua_tostring(L, -1));
+		goto error;
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 503
+	case LUA_ERRGCMM:
+		ha_alert("Lua filter '%s' : garbage collector error : %s", conf->reg->name, lua_tostring(L, -1));
+		goto error;
+#endif
+	default:
+		ha_alert("Lua filter '%s' : unknonwn error : %s", conf->reg->name, lua_tostring(L, -1));
+		goto error;
+	}
+
+	lua_settop(L, 0);
+	return 0;
+
+  error:
+	lua_settop(L, 0);
+	return -1;
+}
+
+static void hlua_filter_deinit_per_thread(struct proxy *px, struct flt_conf *fconf)
+{
+	struct hlua_flt_config *conf = fconf->conf;
+	lua_State *L;
+	int state_id;
+
+	if (!conf)
+		return;
+
+	state_id = reg_flt_to_stack_id(conf->reg);
+	L = hlua_states[state_id];
+	luaL_unref(L, LUA_REGISTRYINDEX, conf->ref[state_id]);
+}
+
+static int hlua_filter_init(struct proxy *px, struct flt_conf *fconf)
+{
+	struct hlua_flt_config *conf = fconf->conf;
+	int state_id = reg_flt_to_stack_id(conf->reg);
+
+	/* Rely on per-thread init for global scripts */
+	if (!state_id)
+		return hlua_filter_init_per_thread(px, fconf);
+	return 0;
+}
+
+static void hlua_filter_deinit(struct proxy *px, struct flt_conf *fconf)
+{
+
+	if (fconf->conf) {
+		struct hlua_flt_config *conf = fconf->conf;
+		int state_id = reg_flt_to_stack_id(conf->reg);
+		int pos;
+
+		/* Rely on per-thread deinit for global scripts */
+		if (!state_id)
+			hlua_filter_deinit_per_thread(px, fconf);
+
+		for (pos = 0; conf->args[pos]; pos++)
+			free(conf->args[pos]);
+		free(conf->args);
+	}
+	ha_free(&fconf->conf);
+	ha_free((char **)&fconf->id);
+	ha_free(&fconf->ops);
+}
+
+static int hlua_filter_new(struct stream *s, struct filter *filter)
+{
+	struct hlua_flt_config *conf = FLT_CONF(filter);
+	struct hlua_flt_ctx *flt_ctx = NULL;
+	int ret = 1;
+
+	/* 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 (!s->hlua) {
+		struct hlua *hlua;
+
+		hlua = pool_alloc(pool_head_hlua);
+		if (!hlua) {
+			SEND_ERR(s->be, "Lua filter '%s': can't initialize Lua context.\n",
+			         conf->reg->name);
+			ret = 0;
+			goto end;
+		}
+		HLUA_INIT(hlua);
+		s->hlua = hlua;
+		if (!hlua_ctx_init(s->hlua, reg_flt_to_stack_id(conf->reg), s->task, 0)) {
+			SEND_ERR(s->be, "Lua filter '%s': can't initialize Lua context.\n",
+			         conf->reg->name);
+			ret = 0;
+			goto end;
+		}
+	}
+
+	flt_ctx = pool_zalloc(pool_head_hlua_flt_ctx);
+	if (!flt_ctx) {
+		SEND_ERR(s->be, "Lua filter '%s': can't initialize filter Lua context.\n",
+			 conf->reg->name);
+		ret = 0;
+		goto end;
+	}
+	flt_ctx->hlua[0] = pool_alloc(pool_head_hlua);
+	flt_ctx->hlua[1] = pool_alloc(pool_head_hlua);
+	if (!flt_ctx->hlua[0] || !flt_ctx->hlua[1]) {
+		SEND_ERR(s->be, "Lua filter '%s': can't initialize filter Lua context.\n",
+			 conf->reg->name);
+		ret = 0;
+		goto end;
+	}
+	HLUA_INIT(flt_ctx->hlua[0]);
+	HLUA_INIT(flt_ctx->hlua[1]);
+	if (!hlua_ctx_init(flt_ctx->hlua[0], reg_flt_to_stack_id(conf->reg), s->task, 0) ||
+	    !hlua_ctx_init(flt_ctx->hlua[1], reg_flt_to_stack_id(conf->reg), s->task, 0)) {
+		SEND_ERR(s->be, "Lua filter '%s': can't initialize filter Lua context.\n",
+			 conf->reg->name);
+		ret = 0;
+		goto end;
+	}
+
+	if (!HLUA_IS_RUNNING(s->hlua)) {
+		/* The following Lua calls can fail. */
+		if (!SET_SAFE_LJMP(s->hlua)) {
+			const char *error;
+
+			if (lua_type(s->hlua->T, -1) == LUA_TSTRING)
+				error = lua_tostring(s->hlua->T, -1);
+			else
+				error = "critical error";
+			SEND_ERR(s->be, "Lua filter '%s': %s.\n", conf->reg->name, error);
+			ret = 0;
+			goto end;
+		}
+
+		/* Check stack size. */
+		if (!lua_checkstack(s->hlua->T, 1)) {
+			SEND_ERR(s->be, "Lua filter '%s': full stack.\n", conf->reg->name);
+			ret = 0;
+			goto end;
+		}
+
+		lua_rawgeti(s->hlua->T, LUA_REGISTRYINDEX, conf->ref[s->hlua->state_id]);
+		if (lua_getfield(s->hlua->T, -1, "new") != LUA_TFUNCTION) {
+			SEND_ERR(s->be, "Lua filter '%s': 'new' field is not a function.\n",
+				 conf->reg->name);
+			RESET_SAFE_LJMP(s->hlua);
+			ret = 0;
+			goto end;
+		}
+		lua_insert(s->hlua->T, -2);
+
+		/* Push the copy on the stack */
+		s->hlua->nargs = 1;
+
+		/* We must initialize the execution timeouts. */
+		s->hlua->max_time = hlua_timeout_session;
+
+		/* At this point the execution is safe. */
+		RESET_SAFE_LJMP(s->hlua);
+	}
+
+	switch (hlua_ctx_resume(s->hlua, 0)) {
+	case HLUA_E_OK:
+		/* Nothing returned or not a table, ignore the filter for current stream */
+		if (!lua_gettop(s->hlua->T) || !lua_istable(s->hlua->T, 1)) {
+			ret = 0;
+			goto end;
+		}
+
+		/* Attached the filter pointer to the ctx */
+		lua_pushstring(s->hlua->T, "__filter");
+		lua_pushlightuserdata(s->hlua->T, filter);
+		lua_settable(s->hlua->T, -3);
+
+		/* Save a ref on the filter ctx */
+		lua_pushvalue(s->hlua->T, 1);
+		flt_ctx->ref = luaL_ref(s->hlua->T, LUA_REGISTRYINDEX);
+		filter->ctx = flt_ctx;
+		break;
+	case HLUA_E_ERRMSG:
+		SEND_ERR(s->be, "Lua filter '%s' : %s.\n", conf->reg->name, lua_tostring(s->hlua->T, -1));
+		ret = -1;
+		goto end;
+	case HLUA_E_ETMOUT:
+		SEND_ERR(s->be, "Lua filter '%s' : 'new' execution timeout.\n", conf->reg->name);
+		ret = 0;
+		goto end;
+	case HLUA_E_NOMEM:
+		SEND_ERR(s->be, "Lua filter '%s' : out of memory error.\n", conf->reg->name);
+		ret = 0;
+		goto end;
+	case HLUA_E_AGAIN:
+	case HLUA_E_YIELD:
+		SEND_ERR(s->be, "Lua filter '%s': yield functions like core.tcp() or core.sleep()"
+			 " are not allowed from 'new' function.\n", conf->reg->name);
+		ret = 0;
+		goto end;
+	case HLUA_E_ERR:
+		SEND_ERR(s->be, "Lua filter '%s': 'new' returns an unknown error.\n", conf->reg->name);
+		ret = 0;
+		goto end;
+	default:
+		ret = 0;
+		goto end;
+	}
+
+  end:
+	if (s->hlua)
+		lua_settop(s->hlua->T, 0);
+	if (ret <= 0) {
+		if (flt_ctx) {
+			hlua_ctx_destroy(flt_ctx->hlua[0]);
+			hlua_ctx_destroy(flt_ctx->hlua[1]);
+			pool_free(pool_head_hlua_flt_ctx, flt_ctx);
+		}
+	}
+	return ret;
+}
+
+static void hlua_filter_delete(struct stream *s, struct filter *filter)
+{
+	struct hlua_flt_ctx *flt_ctx = filter->ctx;
+
+	luaL_unref(s->hlua->T, LUA_REGISTRYINDEX, flt_ctx->ref);
+	hlua_ctx_destroy(flt_ctx->hlua[0]);
+	hlua_ctx_destroy(flt_ctx->hlua[1]);
+	pool_free(pool_head_hlua_flt_ctx, flt_ctx);
+	filter->ctx = NULL;
+}
+
+static int hlua_filter_parse_fct(char **args, int *cur_arg, struct proxy *px,
+				 struct flt_conf *fconf, char **err, void *private)
+{
+	struct hlua_reg_filter *reg_flt = private;
+	lua_State *L;
+	struct hlua_flt_config *conf = NULL;
+	const char *flt_id = NULL;
+	int state_id, pos, flt_flags = 0;
+	struct flt_ops *hlua_flt_ops = NULL;
+
+	state_id = reg_flt_to_stack_id(reg_flt);
+	L = hlua_states[state_id];
+
+	/* Initialize the filter ops with default callbacks */
+	hlua_flt_ops = calloc(1, sizeof(*hlua_flt_ops));
+	if (!hlua_flt_ops) {
+		memprintf(err, "Lua filter '%s' : Lua out of memory error", reg_flt->name);
+		return -1;
+	}
+	hlua_flt_ops->init              = hlua_filter_init;
+	hlua_flt_ops->deinit            = hlua_filter_deinit;
+	if (state_id) {
+		/* Set per-thread callback if script is loaded per-thread */
+		hlua_flt_ops->init_per_thread   = hlua_filter_init_per_thread;
+		hlua_flt_ops->deinit_per_thread = hlua_filter_deinit_per_thread;
+	}
+	hlua_flt_ops->attach            = hlua_filter_new;
+	hlua_flt_ops->detach            = hlua_filter_delete;
+
+	/* Push the filter class on the stack and resolve all callbacks */
+	lua_rawgeti(L, LUA_REGISTRYINDEX, reg_flt->flt_ref[state_id]);
+
+	/*
+	 * XXX: no callback supported for now
+	 */
+
+	/* Get id and flags of the filter class */
+	if (lua_getfield(L, -1, "id") == LUA_TSTRING)
+		flt_id  = lua_tostring(L, -1);
+	lua_pop(L, 1);
+	if (lua_getfield(L, -1, "flags") == LUA_TNUMBER)
+		flt_flags  = lua_tointeger(L, -1);
+	lua_pop(L, 1);
+
+	/* Create the filter config */
+	conf = calloc(1, sizeof(*conf));
+	if (!conf) {
+		memprintf(err, "Lua filter '%s' : Lua out of memory error", reg_flt->name);
+		goto error;
+	}
+	conf->reg = reg_flt;
+
+	/* duplicate args */
+	for (pos = 0; *args[*cur_arg + 1 + pos]; pos++);
+	conf->args = calloc(pos + 1, sizeof(*conf->args));
+	for (pos = 0; *args[*cur_arg + 1 + pos]; pos++)
+		conf->args[pos] = strdup(args[*cur_arg + 1 + pos]);
+	conf->args[pos] = NULL;
+	*cur_arg += pos + 1;
+
+	fconf->id    = strdup(flt_id);
+	fconf->flags = flt_flags;
+	fconf->conf  = conf;
+	fconf->ops   = hlua_flt_ops;
+
+	lua_settop(L, 0);
+	return 0;
+
+  error:
+	free(hlua_flt_ops);
+	free(conf);
+	lua_settop(L, 0);
+	return -1;
+}
+
+/* This function is an LUA binding used for registering a filter. It expects a
+ * fileter name used in the haproxy configuration file and a LUA function to
+ * parse configuration arguments.
+ */
+__LJMP static int hlua_register_filter(lua_State *L)
+{
+	struct buffer *trash;
+	struct flt_kw_list *fkl;
+	struct flt_kw *fkw;
+	const char *name;
+	struct hlua_reg_filter *reg_flt= NULL;
+	int flt_ref, fun_ref;
+	int len;
+
+	MAY_LJMP(check_args(L, 3, "register_filter"));
+
+	/* First argument : filter name. */
+	name = MAY_LJMP(luaL_checkstring(L, 1));
+
+	/* Second argument : The filter class */
+	flt_ref = MAY_LJMP(hlua_checktable(L, 2));
+
+	/* Third argument : lua function. */
+	fun_ref = MAY_LJMP(hlua_checkfunction(L, 3));
+
+	trash = get_trash_chunk();
+	chunk_printf(trash, "lua.%s", name);
+	fkw = flt_find_kw(trash->area);
+	if (fkw != NULL) {
+		reg_flt = fkw->private;
+		if (reg_flt->flt_ref[hlua_state_id] != -1 ||  reg_flt->fun_ref[hlua_state_id] != -1) {
+			ha_warning("Trying to register filter 'lua.%s' more than once. "
+				   "This will become a hard error in version 2.5.\n", name);
+		}
+		reg_flt->flt_ref[hlua_state_id] = flt_ref;
+		reg_flt->fun_ref[hlua_state_id] = fun_ref;
+		return 0;
+	}
+
+	fkl = calloc(1, sizeof(*fkl) + sizeof(struct flt_kw) * 2);
+	if (!fkl)
+		goto alloc_error;
+	fkl->scope = "HLUA";
+
+	reg_flt = new_hlua_reg_filter(name);
+	if (!reg_flt)
+		goto alloc_error;
+
+	reg_flt->flt_ref[hlua_state_id] = flt_ref;
+	reg_flt->fun_ref[hlua_state_id] = fun_ref;
+
+	/* The filter keyword */
+	len = strlen("lua.") + strlen(name) + 1;
+	fkl->kw[0].kw = calloc(1, len);
+	if (!fkl->kw[0].kw)
+		goto alloc_error;
+
+	snprintf((char *)fkl->kw[0].kw, len, "lua.%s", name);
+
+	fkl->kw[0].parse = hlua_filter_parse_fct;
+	fkl->kw[0].private = reg_flt;
+	memset(&fkl->kw[1], 0, sizeof(*fkl->kw));
+
+	/* Register this new filter */
+	flt_register_keywords(fkl);
+
+	return 0;
+
+  alloc_error:
+	release_hlua_reg_filter(reg_flt);
+	ha_free(&fkl);
+	WILL_LJMP(luaL_error(L, "Lua out of memory error."));
+	return 0; /* Never reached */
+}
+
 static int hlua_read_timeout(char **args, int section_type, struct proxy *curpx,
                              const struct proxy *defpx, const char *file, int line,
                              char **err, unsigned int *timeout)
@@ -8837,6 +9348,7 @@
 	int errors;
 	char *err = NULL;
 	struct hlua_function *fcn;
+	struct hlua_reg_filter *reg_flt;
 
 #if USE_OPENSSL
 	/* Initialize SSL server. */
@@ -8924,6 +9436,31 @@
 		}
 	}
 
+	/* Do the same with registered filters */
+	list_for_each_entry(reg_flt, &referenced_filters, l) {
+		ret = 0;
+		for (i = 1; i < global.nbthread + 1; i++) {
+			if (reg_flt->flt_ref[i] == -1)
+				ret--;
+			else
+				ret++;
+		}
+		if (abs(ret) != global.nbthread) {
+			ha_alert("Lua filter '%s' is not referenced in all thread. "
+			         "Expect function in all thread or in none thread.\n", reg_flt->name);
+			errors++;
+			continue;
+		}
+
+		if ((reg_flt->flt_ref[0] == -1) == (ret < 0)) {
+			ha_alert("Lua filter '%s' is referenced both ins shared Lua context (through lua-load) "
+			         "and per-thread Lua context (through lua-load-per-thread). these two context "
+			         "exclusive.\n", fcn->name);
+			errors++;
+		}
+	}
+
+
 	if (errors > 0)
 		return 0;
 
@@ -9060,6 +9597,7 @@
 	hlua_class_function(L, "register_action", hlua_register_action);
 	hlua_class_function(L, "register_service", hlua_register_service);
 	hlua_class_function(L, "register_cli", hlua_register_cli);
+	hlua_class_function(L, "register_filter", hlua_register_filter);
 	hlua_class_function(L, "yield", hlua_yield);
 	hlua_class_function(L, "set_nice", hlua_set_nice);
 	hlua_class_function(L, "sleep", hlua_sleep);
@@ -9104,6 +9642,24 @@
 
 	/*
 	 *
+	 * Create "Filter" object.
+	 *
+	 */
+
+	/* This table entry is the object "filter" base. */
+	lua_newtable(L);
+
+	/* push flags and constants */
+	hlua_class_const_int(L, "CONTINUE", 1);
+	hlua_class_const_int(L, "WAIT",     0);
+	hlua_class_const_int(L, "ERROR",    -1);
+
+	hlua_class_const_int(L, "FLT_CFG_FL_HTX", FLT_CFG_FL_HTX);
+
+	lua_setglobal(L, "filter");
+
+	/*
+	 *
 	 * Register class Map
 	 *
 	 */
@@ -9538,6 +10094,10 @@
 static void hlua_deinit()
 {
 	int thr;
+	struct hlua_reg_filter *reg_flt, *reg_flt_bck;
+
+	list_for_each_entry_safe(reg_flt, reg_flt_bck, &referenced_filters, l)
+		release_hlua_reg_filter(reg_flt);
 
 	for (thr = 0; thr < MAX_THREADS+1; thr++) {
 		if (hlua_states[thr])