MINOR: vars: Delay variable content freeing in var_set function

When calling var_set on a variable of type string (SMP_T_STR, SMP_T_BIN
or SMP_T_METH), the contents of the variable were freed directly. When
adding conditions to set-var calls we might have cases in which the
contents of an existing variable should be kept unchanged so the freeing
of the internal buffers is delayed in the var_set function (so that we
can bypass it later).
This commit is contained in:
Remi Tricot-Le Breton 2021-12-16 17:14:36 +01:00 committed by Willy Tarreau
parent 1bd9805085
commit 25fccd52ac

View File

@ -301,6 +301,25 @@ static int smp_fetch_var(const struct arg *args, struct sample *smp, const char
return vars_get_by_desc(var_desc, smp, def);
}
/*
* Clear the contents of a variable so that it can be reset directly.
* This function is used just before a variable is filled out of a sample's
* content.
*/
static inline void var_clear_buffer(struct sample *smp, struct vars *vars, struct var *var, int var_type)
{
if (var_type == SMP_T_STR || var_type == SMP_T_BIN) {
ha_free(&var->data.u.str.area);
var_accounting_diff(vars, smp->sess, smp->strm,
-var->data.u.str.data);
}
else if (var_type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
ha_free(&var->data.u.meth.str.area);
var_accounting_diff(vars, smp->sess, smp->strm,
-var->data.u.meth.str.data);
}
}
/* This function tries to create a variable whose name hash is <name_hash> in
* scope <scope> and store sample <smp> as its value.
*
@ -321,6 +340,7 @@ static int var_set(uint64_t name_hash, enum vars_scope scope, struct sample *smp
struct vars *vars;
struct var *var;
int ret = 0;
int previous_type = SMP_T_ANY;
vars = get_vars(smp->sess, smp->strm, scope);
if (!vars || vars->scope != scope)
@ -336,19 +356,6 @@ static int var_set(uint64_t name_hash, enum vars_scope scope, struct sample *smp
ret = 1;
goto unlock;
}
/* free its used memory. */
if (var->data.type == SMP_T_STR ||
var->data.type == SMP_T_BIN) {
ha_free(&var->data.u.str.area);
var_accounting_diff(vars, smp->sess, smp->strm,
-var->data.u.str.data);
}
else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
ha_free(&var->data.u.meth.str.area);
var_accounting_diff(vars, smp->sess, smp->strm,
-var->data.u.meth.str.data);
}
} else {
if (flags & VF_UPDATEONLY)
goto unlock;
@ -368,22 +375,27 @@ static int var_set(uint64_t name_hash, enum vars_scope scope, struct sample *smp
}
/* Set type. */
previous_type = var->data.type;
var->data.type = smp->data.type;
/* Copy data. If the data needs memory, the function can fail. */
switch (var->data.type) {
case SMP_T_BOOL:
case SMP_T_SINT:
var_clear_buffer(smp, vars, var, previous_type);
var->data.u.sint = smp->data.u.sint;
break;
case SMP_T_IPV4:
var_clear_buffer(smp, vars, var, previous_type);
var->data.u.ipv4 = smp->data.u.ipv4;
break;
case SMP_T_IPV6:
var_clear_buffer(smp, vars, var, previous_type);
var->data.u.ipv6 = smp->data.u.ipv6;
break;
case SMP_T_STR:
case SMP_T_BIN:
var_clear_buffer(smp, vars, var, previous_type);
if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.str.data)) {
var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
goto unlock;
@ -401,6 +413,7 @@ static int var_set(uint64_t name_hash, enum vars_scope scope, struct sample *smp
var->data.u.str.data);
break;
case SMP_T_METH:
var_clear_buffer(smp, vars, var, previous_type);
var->data.u.meth.meth = smp->data.u.meth.meth;
if (smp->data.u.meth.meth != HTTP_METH_OTHER)
break;