]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: lua: Add `ifexist` parameter to `set_var`
authorTim Duesterhus <tim@bastelstu.be>
Tue, 19 May 2020 11:49:42 +0000 (13:49 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 25 May 2020 06:12:35 +0000 (08:12 +0200)
As discussed in GitHub issue #624 Lua scripts should not use
variables that are never going to be read, because the memory
for variable names is never going to be freed.

Add an optional `ifexist` parameter to the `set_var` function
that allows a Lua developer to set variables that are going to
be ignored if the variable name was not used elsewhere before.

Usually this mean that there is no `var()` sample fetch for the
variable in question within the configuration.

doc/lua-api/index.rst
reg-tests/lua/set_var.lua
reg-tests/lua/set_var.vtc
src/hlua.c

index f3e5cb786b88b91f66fbe1eccf4a2009cfed950a..3d8282cdb3be44e9bfb39d6b90234e5cc606bba3 100644 (file)
@@ -1710,7 +1710,7 @@ TXN class
   :param class_txn txn: The class txn object containing the data.
   :param opaque data: The data which is stored in the transaction.
 
-.. js:function:: TXN.set_var(TXN, var, value)
+.. js:function:: TXN.set_var(TXN, var, value[, ifexist])
 
   Converts a Lua type in a HAProxy type and store it in a variable <var>.
 
@@ -1718,6 +1718,10 @@ TXN class
   :param string var: The variable name according with the HAProxy variable syntax.
   :param type value: The value associated to the variable. The type can be string or
                      integer.
+  :param boolean ifexist: If this parameter is set to a truthy value the variable
+                          will only be set if it was defined elsewhere (i.e. used
+                          within the configuration). It is highly recommended to
+                          always set this to true.
 
 .. js:function:: TXN.unset_var(TXN, var)
 
@@ -2513,7 +2517,7 @@ AppletHTTP class
   :param opaque data: The data which is stored in the transaction.
   :see: :js:func:`AppletHTTP.get_priv`
 
-.. js:function:: AppletHTTP.set_var(applet, var, value)
+.. js:function:: AppletHTTP.set_var(applet, var, value[, ifexist])
 
   Converts a Lua type in a HAProxy type and store it in a variable <var>.
 
@@ -2521,6 +2525,10 @@ AppletHTTP class
   :param string var: The variable name according with the HAProxy variable syntax.
   :param type value: The value associated to the variable. The type ca be string or
                      integer.
+  :param boolean ifexist: If this parameter is set to a truthy value the variable
+                          will only be set if it was defined elsewhere (i.e. used
+                          within the configuration). It is highly recommended to
+                          always set this to true.
   :see: :js:func:`AppletHTTP.unset_var`
   :see: :js:func:`AppletHTTP.get_var`
 
@@ -2624,7 +2632,7 @@ AppletTCP class
   :param opaque data: The data which is stored in the transaction.
   :see: :js:func:`AppletTCP.get_priv`
 
-.. js:function:: AppletTCP.set_var(applet, var, value)
+.. js:function:: AppletTCP.set_var(applet, var, value[, ifexist])
 
   Converts a Lua type in a HAProxy type and stores it in a variable <var>.
 
@@ -2632,6 +2640,10 @@ AppletTCP class
   :param string var: The variable name according with the HAProxy variable syntax.
   :param type value: The value associated to the variable. The type can be string or
                      integer.
+  :param boolean ifexist: If this parameter is set to a truthy value the variable
+                          will only be set if it was defined elsewhere (i.e. used
+                          within the configuration). It is highly recommended to
+                          always set this to true.
   :see: :js:func:`AppletTCP.unset_var`
   :see: :js:func:`AppletTCP.get_var`
 
index 78d1fd495101bf37a26c6de8f329e77a42706fbf..f4d5e7af443f5b43a788456c643a0ee8020de66e 100644 (file)
@@ -10,3 +10,16 @@ core.register_service("set_var", "http", function(applet)
        applet:start_response()
        applet:send("")
 end)
+
+core.register_service("set_var_ifexist", "http", function(applet)
+       local var_name = applet.headers["var"][0]
+       local result = applet:set_var(var_name, "value", true)
+       if result then
+               applet:set_status(202)
+       else
+               applet:set_status(400)
+       end
+       applet:add_header("echo", applet:get_var(var_name) or "(nil)")
+       applet:start_response()
+       applet:send("")
+end)
index 2f1ba4ee91ff5f275dde8f63b81199c93ca3d7e5..5d37fcb88c2d9f5bd04e75adb6f57cba473d8473 100644 (file)
@@ -13,11 +13,20 @@ haproxy h1 -conf {
         bind "fd@${fe1}"
 
         http-request use-service lua.set_var
+
+    frontend fe2
+        mode http
+        ${no-htx} option http-use-htx
+        bind "fd@${fe2}"
+
+        http-request set-header Dummy %[var(txn.fe2_foo)]
+
+        http-request use-service lua.set_var_ifexist
 } -start
 
 client c0 -connect ${h1_fe1_sock} {
     txreq -url "/" \
-        -hdr "Var: txn.foo"
+        -hdr "Var: txn.fe1_foo"
     rxresp
     expect resp.status == 202
     expect resp.http.echo == "value"
@@ -27,3 +36,16 @@ client c0 -connect ${h1_fe1_sock} {
     expect resp.status == 400
     expect resp.http.echo == "(nil)"
 } -run
+
+client c1 -connect ${h1_fe2_sock} {
+    txreq -url "/" \
+        -hdr "Var: txn.fe2_foo"
+    rxresp
+    expect resp.status == 202
+    expect resp.http.echo == "value"
+    txreq -url "/" \
+        -hdr "Var: txn.fe2_bar"
+    rxresp
+    expect resp.status == 400
+    expect resp.http.echo == "(nil)"
+} -run
index 0ce57c7ed553714d20d9fecee0aff45772d461f0..f0d7e0f9e6649fbad6ceef08b3ac4f9fb008fd46 100644 (file)
@@ -3475,7 +3475,8 @@ __LJMP static int hlua_applet_tcp_set_var(lua_State *L)
        size_t len;
        struct sample smp;
 
-       MAY_LJMP(check_args(L, 3, "set_var"));
+       if (lua_gettop(L) < 3 || lua_gettop(L) > 4)
+               WILL_LJMP(luaL_error(L, "'set_var' needs between 3 and 4 arguments"));
 
        /* It is useles to retrieve the stream, but this function
         * runs only in a stream context.
@@ -3489,7 +3490,12 @@ __LJMP static int hlua_applet_tcp_set_var(lua_State *L)
 
        /* Store the sample in a variable. */
        smp_set_owner(&smp, s->be, s->sess, s, 0);
-       lua_pushboolean(L, vars_set_by_name(name, len, &smp) != 0);
+
+       if (lua_gettop(L) == 4 && lua_toboolean(L, 4))
+               lua_pushboolean(L, vars_set_by_name_ifexist(name, len, &smp) != 0);
+       else
+               lua_pushboolean(L, vars_set_by_name(name, len, &smp) != 0);
+
        return 1;
 }
 
@@ -3953,7 +3959,8 @@ __LJMP static int hlua_applet_http_set_var(lua_State *L)
        size_t len;
        struct sample smp;
 
-       MAY_LJMP(check_args(L, 3, "set_var"));
+       if (lua_gettop(L) < 3 || lua_gettop(L) > 4)
+               WILL_LJMP(luaL_error(L, "'set_var' needs between 3 and 4 arguments"));
 
        /* It is useles to retrieve the stream, but this function
         * runs only in a stream context.
@@ -3967,7 +3974,12 @@ __LJMP static int hlua_applet_http_set_var(lua_State *L)
 
        /* Store the sample in a variable. */
        smp_set_owner(&smp, s->be, s->sess, s, 0);
-       lua_pushboolean(L, vars_set_by_name(name, len, &smp) != 0);
+
+       if (lua_gettop(L) == 4 && lua_toboolean(L, 4))
+               lua_pushboolean(L, vars_set_by_name_ifexist(name, len, &smp) != 0);
+       else
+               lua_pushboolean(L, vars_set_by_name(name, len, &smp) != 0);
+
        return 1;
 }
 
@@ -5040,7 +5052,8 @@ __LJMP static int hlua_set_var(lua_State *L)
        size_t len;
        struct sample smp;
 
-       MAY_LJMP(check_args(L, 3, "set_var"));
+       if (lua_gettop(L) < 3 || lua_gettop(L) > 4)
+               WILL_LJMP(luaL_error(L, "'set_var' needs between 3 and 4 arguments"));
 
        /* It is useles to retrieve the stream, but this function
         * runs only in a stream context.
@@ -5053,7 +5066,12 @@ __LJMP static int hlua_set_var(lua_State *L)
 
        /* Store the sample in a variable. */
        smp_set_owner(&smp, htxn->p, htxn->s->sess, htxn->s, htxn->dir & SMP_OPT_DIR);
-       lua_pushboolean(L, vars_set_by_name(name, len, &smp) != 0);
+
+       if (lua_gettop(L) == 4 && lua_toboolean(L, 4))
+               lua_pushboolean(L, vars_set_by_name_ifexist(name, len, &smp) != 0);
+       else
+               lua_pushboolean(L, vars_set_by_name(name, len, &smp) != 0);
+
        return 1;
 }