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