From: Thierry FOURNIER Date: Tue, 7 Apr 2015 09:27:54 +0000 (+0200) Subject: MINOR: lua: map system integration in Lua X-Git-Tag: v1.6-dev2~218 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3def393f8d518172c9000388c4b373f520002909;p=thirdparty%2Fhaproxy.git MINOR: lua: map system integration in Lua This patch cretes a new Map class that permits to do some lookup in HAProxy maps. This Map class is integration in the HAProxy update system, so we can modify the map throught the socket. --- diff --git a/doc/lua-api/index.rst b/doc/lua-api/index.rst index 45fadbcbb4..3c7201860f 100644 --- a/doc/lua-api/index.rst +++ b/doc/lua-api/index.rst @@ -1096,6 +1096,125 @@ Socket class :param class_socket socket: Is the manipulated Socket. :param integer value: The timeout value. +Map class +========= + +.. js:class:: Map + + This class permits to do some lookup in HAProxy maps. The declared maps can + be modified during the runtime throught the HAProxy management socket. + +.. code-block:: lua + + default = "usa" + + -- Create and load map + geo = Map.new("geo.map", Map.ip); + + -- Create new fetch that returns the user country + core.register_fetches("country", function(txn) + local src; + local loc; + + src = txn.f:fhdr("x-forwarded-for"); + if (src == nil) then + src = txn.f:src() + if (src == nil) then + return default; + end + end + + -- Perform lookup + loc = geo:lookup(src); + + if (loc == nil) then + return default; + end + + return loc; + end); + +.. js:attribute:: Map.int + + See the HAProxy configuration.txt file, chapter "Using ACLs and fetching + samples" ans subchapter "ACL basics" to understand this pattern matching + method. + +.. js:attribute:: Map.ip + + See the HAProxy configuration.txt file, chapter "Using ACLs and fetching + samples" ans subchapter "ACL basics" to understand this pattern matching + method. + +.. js:attribute:: Map.str + + See the HAProxy configuration.txt file, chapter "Using ACLs and fetching + samples" ans subchapter "ACL basics" to understand this pattern matching + method. + +.. js:attribute:: Map.beg + + See the HAProxy configuration.txt file, chapter "Using ACLs and fetching + samples" ans subchapter "ACL basics" to understand this pattern matching + method. + +.. js:attribute:: Map.sub + + See the HAProxy configuration.txt file, chapter "Using ACLs and fetching + samples" ans subchapter "ACL basics" to understand this pattern matching + method. + +.. js:attribute:: Map.dir + + See the HAProxy configuration.txt file, chapter "Using ACLs and fetching + samples" ans subchapter "ACL basics" to understand this pattern matching + method. + +.. js:attribute:: Map.dom + + See the HAProxy configuration.txt file, chapter "Using ACLs and fetching + samples" ans subchapter "ACL basics" to understand this pattern matching + method. + +.. js:attribute:: Map.end + + See the HAProxy configuration.txt file, chapter "Using ACLs and fetching + samples" ans subchapter "ACL basics" to understand this pattern matching + method. + +.. js:attribute:: Map.reg + + See the HAProxy configuration.txt file, chapter "Using ACLs and fetching + samples" ans subchapter "ACL basics" to understand this pattern matching + method. + + +.. js:function:: Map.new(file, method) + + Creates and load a map. + + :param string file: Is the file containing the map. + :param integer method: Is the map pattern matching method. See the attributes + of the Map class. + :returns: a class Map object. + :see: The Map attributes. + +.. js:function:: Map.lookup(map, str) + + Perform a lookup in a map. + + :param class_map map: Is the class Map object. + :param string str: Is the string used as key. + :returns: a string containing the result or nil if no match. + +.. js:function:: Map.slookup(map, str) + + Perform a lookup in a map. + + :param class_map map: Is the class Map object. + :param string str: Is the string used as key. + :returns: a string containing the result or empty string if no match. + External Lua libraries ====================== diff --git a/include/proto/map.h b/include/proto/map.h index 907c8ca9e3..9690abb889 100644 --- a/include/proto/map.h +++ b/include/proto/map.h @@ -32,4 +32,7 @@ int map_parse_int(const char *text, struct sample_storage *smp); struct map_reference *map_get_reference(const char *reference); +int sample_load_map(struct arg *arg, struct sample_conv *conv, + const char *file, int line, char **err); + #endif /* _PROTO_PATTERN_H */ diff --git a/include/types/hlua.h b/include/types/hlua.h index 82e80c4fd1..357f7ab138 100644 --- a/include/types/hlua.h +++ b/include/types/hlua.h @@ -16,6 +16,7 @@ #define CLASS_SOCKET "Socket" #define CLASS_CHANNEL "Channel" #define CLASS_HTTP "HTTP" +#define CLASS_MAP "Map" struct stream; diff --git a/src/hlua.c b/src/hlua.c index dc75473e2d..1beafeb017 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,7 @@ static int class_channel_ref; static int class_fetches_ref; static int class_converters_ref; static int class_http_ref; +static int class_map_ref; /* Global Lua execution timeout. By default Lua, execution linked * with stream (actions, sample-fetches and converters) have a @@ -1265,6 +1267,151 @@ static int hlua_set_map(lua_State *L) * __le "<=" */ +/* + * + * + * Class Map + * + * + */ + +/* Returns a struct hlua_map if the stack entry "ud" is + * a class session, otherwise it throws an error. + */ +__LJMP static struct map_descriptor *hlua_checkmap(lua_State *L, int ud) +{ + return (struct map_descriptor *)MAY_LJMP(hlua_checkudata(L, ud, class_map_ref)); +} + +/* This function is the map constructor. It don't need + * the class Map object. It creates and return a new Map + * object. It must be called only during "body" or "init" + * context because it process some filesystem accesses. + */ +__LJMP static int hlua_map_new(struct lua_State *L) +{ + const char *fn; + int match = PAT_MATCH_STR; + struct sample_conv conv; + const char *file = ""; + int line = 0; + lua_Debug ar; + char *err = NULL; + struct arg args[2]; + + if (lua_gettop(L) < 1 || lua_gettop(L) > 2) + WILL_LJMP(luaL_error(L, "'new' needs at least 1 argument.")); + + fn = MAY_LJMP(luaL_checkstring(L, 1)); + + if (lua_gettop(L) >= 2) { + match = MAY_LJMP(luaL_checkinteger(L, 2)); + if (match < 0 || match >= PAT_MATCH_NUM) + WILL_LJMP(luaL_error(L, "'new' needs a valid match method.")); + } + + /* Get Lua filename and line number. */ + if (lua_getstack(L, 1, &ar)) { /* check function at level */ + lua_getinfo(L, "Sl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + file = ar.short_src; + line = ar.currentline; + } + } + + /* fill fake sample_conv struct. */ + conv.kw = ""; /* unused. */ + conv.process = NULL; /* unused. */ + conv.arg_mask = 0; /* unused. */ + conv.val_args = NULL; /* unused. */ + conv.out_type = SMP_T_STR; + conv.private = (void *)(long)match; + switch (match) { + case PAT_MATCH_STR: conv.in_type = SMP_T_STR; break; + case PAT_MATCH_BEG: conv.in_type = SMP_T_STR; break; + case PAT_MATCH_SUB: conv.in_type = SMP_T_STR; break; + case PAT_MATCH_DIR: conv.in_type = SMP_T_STR; break; + case PAT_MATCH_DOM: conv.in_type = SMP_T_STR; break; + case PAT_MATCH_END: conv.in_type = SMP_T_STR; break; + case PAT_MATCH_REG: conv.in_type = SMP_T_STR; break; + case PAT_MATCH_INT: conv.in_type = SMP_T_UINT; break; + case PAT_MATCH_IP: conv.in_type = SMP_T_ADDR; break; + default: + WILL_LJMP(luaL_error(L, "'new' doesn't support this match mode.")); + } + + /* fill fake args. */ + args[0].type = ARGT_STR; + args[0].data.str.str = (char *)fn; + args[1].type = ARGT_STOP; + + /* load the map. */ + if (!sample_load_map(args, &conv, file, line, &err)) { + /* error case: we cant use luaL_error because we must + * free the err variable. + */ + luaL_where(L, 1); + lua_pushfstring(L, "'new': %s.", err); + lua_concat(L, 2); + free(err); + WILL_LJMP(lua_error(L)); + } + + /* create the lua object. */ + lua_newtable(L); + lua_pushlightuserdata(L, args[0].data.map); + lua_rawseti(L, -2, 0); + + /* Pop a class Map metatable and affect it to the userdata. */ + lua_rawgeti(L, LUA_REGISTRYINDEX, class_map_ref); + lua_setmetatable(L, -2); + + + return 1; +} + +__LJMP static inline int _hlua_map_lookup(struct lua_State *L, int str) +{ + struct map_descriptor *desc; + struct pattern *pat; + struct sample smp; + + MAY_LJMP(check_args(L, 2, "lookup")); + desc = MAY_LJMP(hlua_checkmap(L, 1)); + if (desc->pat.expect_type == SMP_T_UINT) { + smp.type = SMP_T_UINT; + smp.data.uint = MAY_LJMP(luaL_checkinteger(L, 2)); + } + else { + smp.type = SMP_T_STR; + smp.flags = SMP_F_CONST; + smp.data.str.str = (char *)MAY_LJMP(luaL_checklstring(L, 2, (size_t *)&smp.data.str.len)); + } + + pat = pattern_exec_match(&desc->pat, &smp, 1); + if (!pat || !pat->smp) { + if (str) + lua_pushstring(L, ""); + else + lua_pushnil(L); + return 1; + } + + /* The Lua pattern must return a string, so we can't check the returned type */ + lua_pushlstring(L, pat->smp->data.str.str, pat->smp->data.str.len); + return 1; +} + +__LJMP static int hlua_map_lookup(struct lua_State *L) +{ + return _hlua_map_lookup(L, 0); +} + +__LJMP static int hlua_map_slookup(struct lua_State *L) +{ + return _hlua_map_lookup(L, 1); +} + /* * * @@ -4571,6 +4718,47 @@ void hlua_init(void) lua_setglobal(gL.T, "core"); + /* + * + * Register class Map + * + */ + + /* This table entry is the object "Map" base. */ + lua_newtable(gL.T); + + /* register pattern types. */ + for (i=0; i