MEDIUM: lua: Add ifexist parameter to set_var

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.
This commit is contained in:
Tim Duesterhus 2020-05-19 13:49:42 +02:00 committed by Christopher Faulet
parent 84ebc136a1
commit 4e172c93f9
4 changed files with 75 additions and 10 deletions

View 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`

View 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)

View 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

View 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;
}