From: Aurelien DARRAGON Date: Tue, 19 Nov 2024 11:01:51 +0000 (+0100) Subject: MINOR: hlua_fcn: implement index and pair metamethods for patref class X-Git-Tag: v3.2-dev1~78 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=202107239103a98fbf643d90d3f2dcaa6fb66927;p=thirdparty%2Fhaproxy.git MINOR: hlua_fcn: implement index and pair metamethods for patref class 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. --- diff --git a/doc/lua-api/index.rst b/doc/lua-api/index.rst index 99d3f213db..9ce713045c 100644 --- a/doc/lua-api/index.rst +++ b/doc/lua-api/index.rst @@ -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 diff --git a/include/haproxy/hlua-t.h b/include/haproxy/hlua-t.h index ce1bc50969..80598f9e93 100644 --- a/include/haproxy/hlua-t.h +++ b/include/haproxy/hlua-t.h @@ -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 ********************/ diff --git a/src/hlua_fcn.c b/src/hlua_fcn.c index 5acb0664a9..315b2dd2e7 100644 --- a/src/hlua_fcn.c +++ b/src/hlua_fcn.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -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. */