]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: hlua_fcn: implement index and pair metamethods for patref class
authorAurelien DARRAGON <adarragon@haproxy.com>
Tue, 19 Nov 2024 11:01:51 +0000 (12:01 +0100)
committerAurelien DARRAGON <adarragon@haproxy.com>
Fri, 29 Nov 2024 06:22:46 +0000 (07:22 +0100)
patref object may now leverage index and pair methamethods to list and
access patref elements at a specific index (=key)

Also, patref:is_map() method may be used to know if the patref stores acl
(key only) or map-style (key:value) patterns.

doc/lua-api/index.rst
include/haproxy/hlua-t.h
src/hlua_fcn.c

index 99d3f213dbbb6ef440deb256ee694a2a7b6fe14c..9ce713045cad7ff927f56b7bc14bcc43e0eba7a7 100644 (file)
@@ -3434,7 +3434,15 @@ Patref class
   (reference) which often is a filename, unless it is prefixed by 'virt@'
   for virtual references or 'opt@' for references that don't necessarily
   point to real file. From Lua, :ref:`patref_class` object may be used to
-  directly manipulate existing pattern reference storage.
+  directly manipulate existing pattern reference storage. For convenience,
+  Patref objects may be directly accessed and listed as a table thanks to
+  index and pairs metamethods. Note however that for the index metamethod,
+  in case of duplicated entries, only the first matching entry is returned.
+
+  .. Warning::
+     Not meant to be shared bewteen multiple contexts. If multiple contexts
+     need to work on the same pattern reference, each context should have
+     its own patref object.
 
   Patref object is obtained using the :js:func:`core.get_patref()`
   function
@@ -3443,6 +3451,11 @@ Patref class
 
   :returns: the name of the pattern reference object.
 
+.. js:function:: Patref.is_map(ref)
+
+  :returns: true if the pattern reference is used to handle maps instead
+   of acl, false otherwise.
+
 .. _applethttp_class:
 
 AppletHTTP class
index ce1bc50969f8c1133ef182f101aab612fe703c0d..80598f9e936c2a6d248d40c9c37b088964d86656 100644 (file)
@@ -234,6 +234,12 @@ struct hlua_server_list_iterator_context {
        struct proxy *px;
 };
 
+struct hlua_patref_iterator_context {
+       struct pat_ref *ref;
+       struct bref bref;       /* back-reference from the pat_ref_elt being accessed
+                                * during listing */
+};
+
 #else /* USE_LUA */
 /************************ For use when Lua is disabled ********************/
 
index 5acb0664a946cc01e8f46b118d8bb3c3e42671f9..315b2dd2e743227650a72db9b0510a4f7d621ae8 100644 (file)
@@ -29,7 +29,7 @@
 #include <haproxy/hlua_fcn.h>
 #include <haproxy/http.h>
 #include <haproxy/net_helper.h>
-#include <haproxy/pattern-t.h>
+#include <haproxy/pattern.h>
 #include <haproxy/protocol.h>
 #include <haproxy/proxy.h>
 #include <haproxy/regex.h>
@@ -2629,6 +2629,17 @@ int hlua_patref_get_name(lua_State *L)
        return 1;
 }
 
+int hlua_patref_is_map(lua_State *L)
+{
+       struct pat_ref *ref;
+
+       ref = hlua_checkudata(L, 1, class_patref_ref);
+       BUG_ON(!ref);
+
+       lua_pushboolean(L, !!(ref->flags & PAT_REF_MAP));
+       return 1;
+}
+
 void hlua_fcn_new_patref(lua_State *L, struct pat_ref *ref)
 {
        lua_newtable(L);
@@ -2646,6 +2657,133 @@ void hlua_fcn_new_patref(lua_State *L, struct pat_ref *ref)
 
        /* set public methods */
        hlua_class_function(L, "get_name", hlua_patref_get_name);
+       hlua_class_function(L, "is_map", hlua_patref_is_map);
+}
+
+int hlua_listable_patref_newindex(lua_State *L) {
+       /* not yet supported */
+       return 0;
+}
+
+/* first arg is the pat_ref
+ * second arg is the required index, in case of duplicate, only the
+ * first matching entry is returned.
+ */
+int hlua_listable_patref_index(lua_State *L)
+{
+       struct pat_ref *ref;
+       const char *key;
+       struct pat_ref_elt *elt;
+
+       ref = hlua_checkudata(L, 1, class_patref_ref);
+       key = luaL_checkstring(L, 2);
+
+       /* Perform pat ref element lookup by key */
+       HA_RWLOCK_WRLOCK(PATREF_LOCK, &ref->lock);
+       elt = pat_ref_find_elt(ref, key);
+       if (elt == NULL) {
+               HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &ref->lock);
+               lua_pushnil(L);
+               return 1;
+       }
+
+       if (elt->sample)
+               lua_pushstring(L, elt->sample);
+       else
+               lua_pushboolean(L, 1); // acl: just push true to tell that the key exists
+       HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &ref->lock);
+
+       return 1;
+}
+
+static int _hlua_listable_patref_pairs_iterator(lua_State *L, int status, lua_KContext ctx)
+{
+       int context_index;
+       struct hlua_patref_iterator_context *hctx;
+       struct pat_ref_elt *elt;
+       int cnt = 0;
+
+       context_index = lua_upvalueindex(1);
+       hctx = lua_touserdata(L, context_index);
+
+       HA_RWLOCK_WRLOCK(PATREF_LOCK, &hctx->ref->lock);
+
+       if (LIST_ISEMPTY(&hctx->bref.users)) {
+               /* first iteration */
+               hctx->bref.ref = hctx->ref->head.n;
+       }
+       else
+               LIST_DEL_INIT(&hctx->bref.users); // drop back ref from previous iteration
+
+ next:
+       /* reached end of list? */
+       if (hctx->bref.ref == &hctx->ref->head) {
+               HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &hctx->ref->lock);
+               lua_pushnil(L);
+               return 1;
+       }
+
+       elt = LIST_ELEM(hctx->bref.ref, struct pat_ref_elt *, list);
+
+       if (elt->gen_id != hctx->ref->curr_gen) {
+               /* check if we may do something to try to prevent thread contention,
+                * unless we run from body/init state where hlua_yieldk is no-op
+                */
+               if (cnt > 10000 && hlua_gethlua(L)) {
+                       /* let's yield and wait for being called again to continue where we left off */
+                       LIST_APPEND(&elt->back_refs, &hctx->bref.users);
+                       HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &hctx->ref->lock);
+                       hlua_yieldk(L, 0, 0, _hlua_listable_patref_pairs_iterator, TICK_ETERNITY, HLUA_CTRLYIELD); // continue
+                       return 0; // not reached
+               }
+
+               hctx->bref.ref = elt->list.n;
+               cnt++;
+               goto next;
+       }
+
+       LIST_APPEND(&elt->back_refs, &hctx->bref.users);
+       HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &hctx->ref->lock);
+
+       hctx->bref.ref = elt->list.n;
+
+       lua_pushstring(L, elt->pattern);
+       if (elt->sample)
+               lua_pushstring(L, elt->sample);
+       else
+               return 1;
+       return 2;
+
+}
+/* iterator must return key as string and value as patref
+ * element value (as string), if we reach end of list, it
+ * returns nil. The context knows the last returned patref's
+ * value. if the context contains patref_elem == NULL, we
+ * start enumeration. We use pat_ref element iterator logic
+ * to iterate through the list.
+ */
+int hlua_listable_patref_pairs_iterator(lua_State *L)
+{
+       return _hlua_listable_patref_pairs_iterator(L, LUA_OK, 0);
+}
+
+/* init the iterator context, return iterator function
+ * with context as closure. The only argument is a
+ * patref list object.
+ */
+int hlua_listable_patref_pairs(lua_State *L)
+{
+       struct hlua_patref_iterator_context *ctx;
+       struct pat_ref *ref;
+
+       ref = hlua_checkudata(L, 1, class_patref_ref);
+
+       ctx = lua_newuserdata(L, sizeof(*ctx));
+       ctx->ref = ref;
+       LIST_INIT(&ctx->bref.users);
+
+       lua_pushcclosure(L, hlua_listable_patref_pairs_iterator, 1);
+       return 1;
 }
 
 void hlua_fcn_reg_core_fcn(lua_State *L)
@@ -2708,6 +2846,9 @@ void hlua_fcn_reg_core_fcn(lua_State *L)
 
        /* Create patref object. */
        lua_newtable(L);
+       hlua_class_function(L, "__index", hlua_listable_patref_index);
+       hlua_class_function(L, "__newindex", hlua_listable_patref_newindex);
+       hlua_class_function(L, "__pairs", hlua_listable_patref_pairs);
        class_patref_ref = hlua_register_metatable(L, CLASS_PATREF);
 
        /* Create server object. */