Ok thank you for your opinions. The patch set is in attachment. Thierry
On Wed, 23 Sep 2015 19:50:24 +0200 Willy Tarreau <[email protected]> wrote: > On Wed, Sep 23, 2015 at 07:37:56PM +0200, Thierry FOURNIER wrote: > > The pending "use-service" code already embbed some changes in the > > généric actions, like requirement of opaque data. So the change is easy > > and the risk of regressions is low. In fact, the main complicated code > > in the Lua is the runtime code and not the configuration parser nor > > the registration of functions. > > OK, thanks for clarifying. Then indeed we should mostly focus on code > reliability and correct behaviour and I agree that your proposal goes > into that direction. > > Willy > >
>From b8698194be79e095741a28e5a58dfacad80e2636 Mon Sep 17 00:00:00 2001 From: Thierry FOURNIER <[email protected]> Date: Tue, 22 Sep 2015 18:26:42 +0200 Subject: [PATCH 1/3] MINOR: action: add private configuration This private configuration pointer is used for storing some configuration data associated the keyword, So many keywords can use the same parse function, and this one can use a discriminator. --- include/types/action.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/types/action.h b/include/types/action.h index 2461f79..d74e5ba 100644 --- a/include/types/action.h +++ b/include/types/action.h @@ -154,6 +154,7 @@ struct action_kw { enum act_parse_ret (*parse)(const char **args, int *cur_arg, struct proxy *px, struct act_rule *rule, char **err); int match_pfx; + void *private; }; struct action_kw_list { -- 2.1.4
>From fd4321b3824e84b789e2441e73399bf6c540e710 Mon Sep 17 00:00:00 2001 From: Thierry FOURNIER <[email protected]> Date: Tue, 22 Sep 2015 19:14:35 +0200 Subject: [PATCH 2/3] MINOR: action: add reference to the original keywork matched for the called parser. This is usefull because the keyword can contains some condifiguration data set while the keyword registration. --- include/types/action.h | 1 + src/proto_http.c | 2 ++ src/proto_tcp.c | 3 +++ 3 files changed, 6 insertions(+) diff --git a/include/types/action.h b/include/types/action.h index d74e5ba..b7e1063 100644 --- a/include/types/action.h +++ b/include/types/action.h @@ -93,6 +93,7 @@ struct act_rule { short deny_status; /* HTTP status to return to user when denying */ enum act_return (*action_ptr)(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *s); /* ptr to custom action */ + struct action_kw *kw; union { struct { char *realm; diff --git a/src/proto_http.c b/src/proto_http.c index 57cc354..9acf0a2 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -9442,6 +9442,7 @@ struct act_rule *parse_http_req_cond(const char **args, const char *file, int li cur_arg = 1; /* try in the module list */ rule->from = ACT_F_HTTP_REQ; + rule->kw = custom; if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) { Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); @@ -9798,6 +9799,7 @@ struct act_rule *parse_http_res_cond(const char **args, const char *file, int li cur_arg = 1; /* try in the module list */ rule->from = ACT_F_HTTP_RES; + rule->kw = custom; if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) { Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); diff --git a/src/proto_tcp.c b/src/proto_tcp.c index e671e41..2ea7161 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -1435,6 +1435,7 @@ static int tcp_parse_response_rule(char **args, int arg, int section_type, if (kw) { arg++; rule->from = ACT_F_TCP_RES_CNT; + rule->kw = kw; if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR) return -1; } else { @@ -1636,9 +1637,11 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type, struct action_kw *kw; if (where & SMP_VAL_FE_CON_ACC) { kw = tcp_req_conn_action(args[arg]); + rule->kw = kw; rule->from = ACT_F_TCP_REQ_CON; } else { kw = tcp_req_cont_action(args[arg]); + rule->kw = kw; rule->from = ACT_F_TCP_REQ_CNT; } if (kw) { -- 2.1.4
>From 86dc0a15d0545731c02022c837a9407ec9f5910f Mon Sep 17 00:00:00 2001 From: Thierry FOURNIER <[email protected]> Date: Wed, 23 Sep 2015 21:03:35 +0200 Subject: [PATCH 3/3] 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. --- doc/lua-api/index.rst | 46 +++++++++++++++++ src/hlua.c | 140 +++++++++++++++++++++++++++++++------------------- 2 files changed, 134 insertions(+), 52 deletions(-) diff --git a/doc/lua-api/index.rst b/doc/lua-api/index.rst index fa69e1f..bbefa84 100644 --- a/doc/lua-api/index.rst +++ b/doc/lua-api/index.rst @@ -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 diff --git a/src/hlua.c b/src/hlua.c index 9651b7f..97adb51 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -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); -- 2.1.4

