]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: lua: add bindings for tcp and http actions
authorThierry FOURNIER <tfournier@haproxy.com>
Mon, 16 Feb 2015 19:23:40 +0000 (20:23 +0100)
committerWilly Tarreau <w@1wt.eu>
Sat, 28 Feb 2015 22:12:35 +0000 (23:12 +0100)
This patch adds the runtime environment for http and tcp actions.
It provides also the function for action registering.

include/types/hlua.h
src/hlua.c

index a4d14442bdec63340002cdb8949491ef198d390c..20ee080dd9d96259bc81f0c38507e8548dcbb0f7 100644 (file)
@@ -61,6 +61,17 @@ struct hlua_function {
        int function_ref;
 };
 
+/* This struct is used with the structs:
+ *  - http_req_rule
+ *  - http_res_rule
+ *  - tcp_rule
+ * It contains the lua execution configuration.
+ */
+struct hlua_rule {
+       struct hlua_function fcn;
+       char **args;
+};
+
 /* This struct contains the pointer provided on the most
  * of internal HAProxy calls during the processing of
  * rules, converters and sample-fetches. This struct is
index e8907bef7717c5bc765e5ef11a4d1b6033f7fa33..f5634459bf92c19282cd21726150dd3d20f7f7f5 100644 (file)
@@ -14,6 +14,7 @@
 #include <proto/hdr_idx.h>
 #include <proto/payload.h>
 #include <proto/proto_http.h>
+#include <proto/proto_tcp.h>
 #include <proto/sample.h>
 #include <proto/task.h>
 
@@ -1383,6 +1384,228 @@ __LJMP static int hlua_register_fetches(lua_State *L)
        return 0;
 }
 
+/* global {tcp|http}-request parser. Return 1 in succes case, else return 0. */
+static int hlua_parse_rule(const char **args, int *cur_arg, struct proxy *px,
+                           struct hlua_rule **rule_p, char **err)
+{
+       struct hlua_rule *rule;
+
+       /* Memory for the rule. */
+       rule = malloc(sizeof(*rule));
+       if (!rule) {
+               memprintf(err, "out of memory error");
+               return 0;
+       }
+       *rule_p = rule;
+
+       /* The requiered arg is a function name. */
+       if (!args[*cur_arg]) {
+               memprintf(err, "expect Lua function name");
+               return 0;
+       }
+
+       /* 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 0;
+       }
+       if (!lua_isfunction(gL.T, -1)) {
+               lua_pop(gL.T, 1);
+               memprintf(err, "'%s' is not a function",  args[*cur_arg]);
+               return 0;
+       }
+
+       /* Reference the Lua function and store the reference. */
+       rule->fcn.function_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX);
+       rule->fcn.name = strdup(args[*cur_arg]);
+       if (!rule->fcn.name) {
+               memprintf(err, "out of memory error.");
+               return 0;
+       }
+       (*cur_arg)++;
+
+       /* TODO: later accept arguments. */
+       rule->args = NULL;
+
+       return 1;
+}
+
+/* This function is a wrapper to execute each LUA function declared
+ * as an action wrapper during the initialisation period. This function
+ * return 1 if the processing is finished (with oe without error) and
+ * return 0 if the function must be called again because the LUA
+ * returns a yield.
+ */
+static int hlua_request_act_wrapper(struct hlua_rule *rule, struct proxy *px,
+                                    struct session *s, struct http_txn *http_txn,
+                                    unsigned int analyzer)
+{
+       char **arg;
+
+       /* If it is the first run, initialize the data for the call. */
+       if (s->hlua.state == HLUA_STOP) {
+               /* Check stack available size. */
+               if (!lua_checkstack(s->hlua.T, 1)) {
+                       send_log(px, LOG_ERR, "Lua function '%s': full stack.", rule->fcn.name);
+                       if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
+                               Alert("Lua function '%s': full stack.\n", rule->fcn.name);
+                       return 0;
+               }
+
+               /* Restore the function in the stack. */
+               lua_rawgeti(s->hlua.T, LUA_REGISTRYINDEX, rule->fcn.function_ref);
+
+               /* Create and and push object session in the stack. */
+               if (!hlua_txn_new(s->hlua.T, s, px, http_txn)) {
+                       send_log(px, LOG_ERR, "Lua function '%s': full stack.", rule->fcn.name);
+                       if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
+                               Alert("Lua function '%s': full stack.\n", rule->fcn.name);
+                       return 0;
+               }
+               s->hlua.nargs = 1;
+
+               /* push keywords in the stack. */
+               for (arg = rule->args; arg && *arg; arg++) {
+                       if (!lua_checkstack(s->hlua.T, 1)) {
+                               send_log(px, LOG_ERR, "Lua function '%s': full stack.", rule->fcn.name);
+                               if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
+                                       Alert("Lua function '%s': full stack.\n", rule->fcn.name);
+                               return 0;
+                       }
+                       lua_pushstring(s->hlua.T, *arg);
+                       s->hlua.nargs++;
+               }
+
+               /* Set the currently running flag. */
+               s->hlua.state = HLUA_RUN;
+       }
+
+       /* Execute the function. */
+       switch (hlua_ctx_resume(&s->hlua, 1)) {
+       /* finished. */
+       case HLUA_E_OK:
+               return 1;
+
+       /* yield. */
+       case HLUA_E_AGAIN:
+               /* Some actions can be wake up when a "write" event
+                * is detected on a response channel. This is useful
+                * only for actions targetted on the requests.
+                */
+               if (analyzer & (AN_REQ_INSPECT_FE|AN_REQ_HTTP_PROCESS_FE)) {
+                       s->rep->flags |= CF_WAKE_WRITE;
+                       s->rep->analysers |= analyzer;
+               }
+               return 0;
+
+       /* finished with error. */
+       case HLUA_E_ERRMSG:
+               /* Display log. */
+               send_log(px, LOG_ERR, "Lua function '%s': %s.", rule->fcn.name, lua_tostring(s->hlua.T, -1));
+               if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
+                       Alert("Lua function '%s': %s.\n", rule->fcn.name, lua_tostring(s->hlua.T, -1));
+               lua_pop(s->hlua.T, 1);
+               return 1;
+
+       case HLUA_E_ERR:
+               /* Display log. */
+               send_log(px, LOG_ERR, "Lua function '%s' return an unknown error.", rule->fcn.name);
+               if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
+                       Alert("Lua function '%s' return an unknown error.\n", rule->fcn.name);
+
+       default:
+               return 1;
+       }
+}
+
+/* Lua execution wrapper for "tcp-request". This function uses
+ * "hlua_request_act_wrapper" for executing the LUA code.
+ */
+int hlua_tcp_req_act_wrapper(struct tcp_rule *tcp_rule, struct proxy *px,
+                             struct session *s)
+{
+       return hlua_request_act_wrapper((struct hlua_rule *)tcp_rule->act_prm.data,
+                                       px, s, NULL, AN_REQ_INSPECT_FE);
+}
+
+/* Lua execution wrapper for "tcp-response". This function uses
+ * "hlua_request_act_wrapper" for executing the LUA code.
+ */
+int hlua_tcp_res_act_wrapper(struct tcp_rule *tcp_rule, struct proxy *px,
+                             struct session *s)
+{
+       return hlua_request_act_wrapper((struct hlua_rule *)tcp_rule->act_prm.data,
+                                       px, s, NULL, AN_RES_INSPECT);
+}
+
+/* Lua execution wrapper for http-request.
+ * This function uses "hlua_request_act_wrapper" for executing
+ * the LUA code.
+ */
+int hlua_http_req_act_wrapper(struct http_req_rule *rule, struct proxy *px,
+                              struct session *s, struct http_txn *http_txn)
+{
+       return hlua_request_act_wrapper((struct hlua_rule *)rule->arg.data, px,
+                                       s, http_txn, AN_REQ_HTTP_PROCESS_FE);
+}
+
+/* Lua execution wrapper for http-response.
+ * This function uses "hlua_request_act_wrapper" for executing
+ * the LUA code.
+ */
+int hlua_http_res_act_wrapper(struct http_res_rule *rule, struct proxy *px,
+                              struct session *s, struct http_txn *http_txn)
+{
+       return hlua_request_act_wrapper((struct hlua_rule *)rule->arg.data, px,
+                                       s, http_txn, AN_RES_HTTP_PROCESS_BE);
+}
+
+/* tcp-request <*> configuration wrapper. */
+static int tcp_req_action_register_lua(const char **args, int *cur_arg, struct proxy *px,
+                                       struct tcp_rule *rule, char **err)
+{
+       if (!hlua_parse_rule(args, cur_arg, px, (struct hlua_rule **)&rule->act_prm.data, err))
+               return -1;
+       rule->action = TCP_ACT_CUSTOM;
+       rule->action_ptr = hlua_tcp_req_act_wrapper;
+       return 1;
+}
+
+/* tcp-response <*> configuration wrapper. */
+static int tcp_res_action_register_lua(const char **args, int *cur_arg, struct proxy *px,
+                                       struct tcp_rule *rule, char **err)
+{
+       if (!hlua_parse_rule(args, cur_arg, px, (struct hlua_rule **)&rule->act_prm.data, err))
+               return -1;
+       rule->action = TCP_ACT_CUSTOM;
+       rule->action_ptr = hlua_tcp_res_act_wrapper;
+       return 1;
+}
+
+/* http-request <*> configuration wrapper. */
+static int http_req_action_register_lua(const char **args, int *cur_arg, struct proxy *px,
+                                        struct http_req_rule *rule, char **err)
+{
+       if (!hlua_parse_rule(args, cur_arg, px, (struct hlua_rule **)&rule->arg.data, err))
+               return -1;
+       rule->action = HTTP_REQ_ACT_CUSTOM_CONT;
+       rule->action_ptr = hlua_http_req_act_wrapper;
+       return 1;
+}
+
+/* http-response <*> configuration wrapper. */
+static int http_res_action_register_lua(const char **args, int *cur_arg, struct proxy *px,
+                                        struct http_res_rule *rule, char **err)
+{
+       if (!hlua_parse_rule(args, cur_arg, px, (struct hlua_rule **)&rule->arg.data, err))
+               return -1;
+       rule->action = HTTP_RES_ACT_CUSTOM_CONT;
+       rule->action_ptr = hlua_http_res_act_wrapper;
+       return 1;
+}
+
 /* This function is called by the main configuration key "lua-load". It loads and
  * execute an lua file during the parsing of the HAProxy configuration file. It is
  * the main lua entry point.
@@ -1442,6 +1665,26 @@ static struct cfg_kw_list cfg_kws = {{ },{
        { 0, NULL, NULL },
 }};
 
+static struct http_req_action_kw_list http_req_kws = {"lua", { }, {
+       { "lua", http_req_action_register_lua },
+       { NULL, NULL }
+}};
+
+static struct http_res_action_kw_list http_res_kws = {"lua", { }, {
+       { "lua", http_res_action_register_lua },
+       { NULL, NULL }
+}};
+
+static struct tcp_action_kw_list tcp_req_cont_kws = {"lua", { }, {
+       { "lua", tcp_req_action_register_lua },
+       { NULL, NULL }
+}};
+
+static struct tcp_action_kw_list tcp_res_cont_kws = {"lua", { }, {
+       { "lua", tcp_res_action_register_lua },
+       { NULL, NULL }
+}};
+
 int hlua_post_init()
 {
        struct hlua_init_function *init;
@@ -1485,6 +1728,12 @@ 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.state = HLUA_STOP;