MINOR: lua: change actions registration

The current Lua action are not registered. The executed function is
selected according with a function name writed in the HAProxy configuration.

This patch add an action registration function. The configuration mode
described above disappear.

This change make some incompatibilities with existing configuration files for
HAProxy 1.6-dev.
This commit is contained in:
Thierry FOURNIER 2015-09-23 21:03:35 +02:00 committed by Willy Tarreau
parent 85c6c97830
commit 8255a75e08
2 changed files with 134 additions and 52 deletions

View File

@ -264,6 +264,52 @@ Core class
:param integer milliseconds: the required milliseconds.
.. js:function:: core.register_action(name, actions, func)
**context**: body
Register an Lua function executed as action. All the registered action can be
used in HAProxy with the prefix "lua.". An action gets a TXN object class as
input.
:param string name: is the name of the converter.
:param table actions: is a table of string describing the HAProxy actions who
want to register to. The expected actions are 'tcp-req',
'tcp-res', 'http-req' or 'http-res'.
:param function func: is the Lua function called to work as converter.
The prototype of the Lua function used as argument is:
.. code-block:: lua
function(txn)
..
* **txn** (*class TXN*): this is a TXN object used for manipulating the
current request or TCP stream.
Here, an exemple of action registration. the action juste send à 'Hello world'
in the logs.
.. code-block:: lua
core.register_action("hello-world", { "tcp-req", "http-req" }, function(txn)
txn:Info("Hello world")
end)
..
This example code is used in HAproxy configuration like this:
::
frontend tcp_frt
mode tcp
tcp-request content lua.hello-world
frontend http_frt
mode http
http-request lua.hello-world
.. js:function:: core.register_converters(name, func)
**context**: body

View File

@ -4505,6 +4505,8 @@ static enum act_return hlua_action(struct act_rule *rule, struct proxy *px,
static enum act_parse_ret action_register_lua(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 = malloc(sizeof(*rule->arg.hlua_rule));
if (!rule->arg.hlua_rule) {
@ -4512,33 +4514,8 @@ static enum act_parse_ret action_register_lua(const char **args, int *cur_arg, s
return ACT_RET_PRS_ERR;
}
/* The requiered arg is a function name. */
if (!args[*cur_arg]) {
memprintf(err, "expect Lua function name");
return ACT_RET_PRS_ERR;
}
/* Lookup for the symbol, and check if it is a function. */
lua_getglobal(gL.T, args[*cur_arg]);
if (lua_isnil(gL.T, -1)) {
lua_pop(gL.T, 1);
memprintf(err, "Lua function '%s' not found", args[*cur_arg]);
return ACT_RET_PRS_ERR;
}
if (!lua_isfunction(gL.T, -1)) {
lua_pop(gL.T, 1);
memprintf(err, "'%s' is not a function", args[*cur_arg]);
return ACT_RET_PRS_ERR;
}
/* Reference the Lua function and store the reference. */
rule->arg.hlua_rule->fcn.function_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX);
rule->arg.hlua_rule->fcn.name = strdup(args[*cur_arg]);
if (!rule->arg.hlua_rule->fcn.name) {
memprintf(err, "out of memory error.");
return ACT_RET_PRS_ERR;
}
(*cur_arg)++;
rule->arg.hlua_rule->fcn = *fcn;
/* TODO: later accept arguments. */
rule->arg.hlua_rule->args = NULL;
@ -4548,6 +4525,90 @@ static enum act_parse_ret action_register_lua(const char **args, int *cur_arg, s
return ACT_RET_PRS_OK;
}
/* 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_action(lua_State *L)
{
struct action_kw_list *akl;
const char *name;
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. */
if (lua_type(L, 2) != LUA_TTABLE)
WILL_LJMP(luaL_error(L, "register_action: second argument must be a table of strings"));
/* Third argument : lua function. */
ref = MAY_LJMP(hlua_checkfunction(L, 3));
/* browse the second argulent as an array. */
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
if (lua_type(L, -1) != LUA_TSTRING)
WILL_LJMP(luaL_error(L, "register_action: second argument must be a table of strings"));
/* Check required environment. Only accepted "http" or "tcp". */
/* Allocate and fill the sample fetch keyword struct. */
akl = malloc(sizeof(*akl) + sizeof(struct action_kw) * 2);
if (!akl)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
fcn = malloc(sizeof(*fcn));
if (!fcn)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
/* Fill fcn. */
fcn->name = strdup(name);
if (!fcn->name)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
fcn->function_ref = ref;
/* List head */
akl->list.n = akl->list.p = NULL;
/* End of array. */
memset(&akl->kw[1], 0, sizeof(*akl->kw));
/* action keyword. */
len = strlen("lua.") + strlen(name) + 1;
akl->kw[0].kw = malloc(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);
akl->kw[0].match_pfx = 0;
akl->kw[0].private = fcn;
akl->kw[0].parse = action_register_lua;
/* select the action registering point. */
if (strcmp(lua_tostring(L, -1), "tcp-req") == 0)
tcp_req_cont_keywords_register(akl);
else if (strcmp(lua_tostring(L, -1), "tcp-res") == 0)
tcp_res_cont_keywords_register(akl);
else if (strcmp(lua_tostring(L, -1), "http-req") == 0)
http_req_keywords_register(akl);
else if (strcmp(lua_tostring(L, -1), "http-res") == 0)
http_res_keywords_register(akl);
else
WILL_LJMP(luaL_error(L, "lua action environment '%s' is unknown. "
"'tcp-req', 'tcp-res', 'http-req' or 'http-res' "
"are expected.", lua_tostring(L, -1)));
/* pop the environment string. */
lua_pop(L, 1);
}
return 0;
}
static int hlua_read_timeout(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err, unsigned int *timeout)
@ -4678,26 +4739,6 @@ static struct cfg_kw_list cfg_kws = {{ },{
{ 0, NULL, NULL },
}};
static struct action_kw_list http_req_kws = { { }, {
{ "lua", action_register_lua },
{ NULL, NULL }
}};
static struct action_kw_list http_res_kws = { { }, {
{ "lua", action_register_lua },
{ NULL, NULL }
}};
static struct action_kw_list tcp_req_cont_kws = { { }, {
{ "lua", action_register_lua },
{ NULL, NULL }
}};
static struct action_kw_list tcp_res_cont_kws = { { }, {
{ "lua", action_register_lua },
{ NULL, NULL }
}};
/* This function can fail with an abort() due to an Lua critical error.
* We are in the initialisation process of HAProxy, this abort() is
* tolerated.
@ -4799,12 +4840,6 @@ void hlua_init(void)
/* Register configuration keywords. */
cfg_register_keywords(&cfg_kws);
/* Register custom HTTP rules. */
http_req_keywords_register(&http_req_kws);
http_res_keywords_register(&http_res_kws);
tcp_req_cont_keywords_register(&tcp_req_cont_kws);
tcp_res_cont_keywords_register(&tcp_res_cont_kws);
/* Init main lua stack. */
gL.Mref = LUA_REFNIL;
gL.flags = 0;
@ -4843,6 +4878,7 @@ void hlua_init(void)
hlua_class_function(gL.T, "register_task", hlua_register_task);
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, "yield", hlua_yield);
hlua_class_function(gL.T, "set_nice", hlua_set_nice);
hlua_class_function(gL.T, "sleep", hlua_sleep);