]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: lua: map system integration in Lua
authorThierry FOURNIER <tfournier@haproxy.com>
Tue, 7 Apr 2015 09:27:54 +0000 (11:27 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 7 Apr 2015 13:56:21 +0000 (15:56 +0200)
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.

doc/lua-api/index.rst
include/proto/map.h
include/types/hlua.h
src/hlua.c
src/map.c

index 45fadbcbb467331d38aa2cb003ce5428c8320e6a..3c7201860f5c100d6476c163f31e88915fb1fcd6 100644 (file)
@@ -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
 ======================
 
index 907c8ca9e3e8735adad4919c172df659e21ce88d..9690abb889bfd12453cb2771f08493d785723397 100644 (file)
@@ -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 */
index 82e80c4fd1f30e99c0c345af4f530092ef28813b..357f7ab1380a01cfb32ba75eec5d5b5288b0d0ec 100644 (file)
@@ -16,6 +16,7 @@
 #define CLASS_SOCKET   "Socket"
 #define CLASS_CHANNEL  "Channel"
 #define CLASS_HTTP     "HTTP"
+#define CLASS_MAP      "Map"
 
 struct stream;
 
index dc75473e2da18637e5b4bbe97cf548929853a4b6..1beafeb017f0ca47f5bc6410767c8b30a9ad7cc3 100644 (file)
@@ -23,6 +23,7 @@
 #include <proto/channel.h>
 #include <proto/hdr_idx.h>
 #include <proto/hlua.h>
+#include <proto/map.h>
 #include <proto/obj_type.h>
 #include <proto/pattern.h>
 #include <proto/payload.h>
@@ -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<PAT_MATCH_NUM; i++)
+               hlua_class_const_int(gL.T, pat_match_names[i], i);
+
+       /* register constructor. */
+       hlua_class_function(gL.T, "new", hlua_map_new);
+
+       /* Create and fill the metatable. */
+       lua_newtable(gL.T);
+
+       /* Create and fille the __index entry. */
+       lua_pushstring(gL.T, "__index");
+       lua_newtable(gL.T);
+
+       /* Register . */
+       hlua_class_function(gL.T, "lookup", hlua_map_lookup);
+       hlua_class_function(gL.T, "slookup", hlua_map_slookup);
+
+       lua_settable(gL.T, -3);
+
+       /* Register previous table in the registry with reference and named entry. */
+       lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
+       lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
+       lua_setfield(gL.T, LUA_REGISTRYINDEX, CLASS_MAP); /* register class session. */
+       class_map_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX); /* reference class session. */
+
+       /* Assign the metatable to the mai Map object. */
+       lua_setmetatable(gL.T, -2);
+
+       /* Set a name to the table. */
+       lua_setglobal(gL.T, "Map");
+
        /*
         *
         * Register class Channel
index 3fb84ac9fb46f3d90f407176c384373c1fcce30f..eea3f4fbc225361fb8cd78f09f7a66476e6c1543 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -114,8 +114,8 @@ static struct map_descriptor *map_create_descriptor(struct sample_conv *conv)
  * This function choose the indexation type (ebtree or list) according with
  * the type of match needed.
  */
-static int sample_load_map(struct arg *arg, struct sample_conv *conv,
-                           const char *file, int line, char **err)
+int sample_load_map(struct arg *arg, struct sample_conv *conv,
+                    const char *file, int line, char **err)
 {
        struct map_descriptor *desc;