MINOR: vars: Add 'unset-var' action/converter

It does the opposite of 'set-var' action/converter. It is really useful for
per-process variables. But, it can be used for any scope.

The lua function 'unset_var' has also been added.
This commit is contained in:
Christopher Faulet 2016-11-09 16:54:56 +01:00 committed by Willy Tarreau
parent ff2613ed7a
commit 85d79c94a9
5 changed files with 212 additions and 35 deletions

View File

@ -3655,6 +3655,7 @@ http-request { allow | tarpit | auth [realm <realm>] | redirect <rule> |
del-map(<file name>) <key fmt> | del-map(<file name>) <key fmt> |
set-map(<file name>) <key fmt> <value fmt> | set-map(<file name>) <key fmt> <value fmt> |
set-var(<var name>) <expr> | set-var(<var name>) <expr> |
unset-var(<var name>) |
{ track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] | { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] |
sc-inc-gpc0(<sc-id>) | sc-inc-gpc0(<sc-id>) |
sc-set-gpt0(<sc-id>) <int> | sc-set-gpt0(<sc-id>) <int> |
@ -3962,6 +3963,13 @@ http-request { allow | tarpit | auth [realm <realm>] | redirect <rule> |
http-request set-var(req.my_var) req.fhdr(user-agent),lower http-request set-var(req.my_var) req.fhdr(user-agent),lower
- unset-var(<var-name>) :
Is used to unset a variable. See above for details about <var-name>.
Example:
http-request unset-var(req.my_var)
- set-src <expr> : - set-src <expr> :
Is used to set the source IP address to the value of specified Is used to set the source IP address to the value of specified
expression. Useful when a proxy in front of HAProxy rewrites source IP, expression. Useful when a proxy in front of HAProxy rewrites source IP,
@ -4119,6 +4127,7 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
del-map(<file name>) <key fmt> | del-map(<file name>) <key fmt> |
set-map(<file name>) <key fmt> <value fmt> | set-map(<file name>) <key fmt> <value fmt> |
set-var(<var-name>) <expr> | set-var(<var-name>) <expr> |
unset-var(<var-name>) |
{ track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] | { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] |
sc-inc-gpc0(<sc-id>) | sc-inc-gpc0(<sc-id>) |
sc-set-gpt0(<sc-id>) <int> | sc-set-gpt0(<sc-id>) <int> |
@ -4328,6 +4337,13 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
http-response set-var(sess.last_redir) res.hdr(location) http-response set-var(sess.last_redir) res.hdr(location)
- unset-var(<var-name>) :
Is used to unset a variable. See above for details about <var-name>.
Example:
http-response unset-var(sess.last_redir)
- { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] : - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] :
enables tracking of sticky counters from current response. Please refer to enables tracking of sticky counters from current response. Please refer to
"http-request track-sc" for a complete description. The only difference "http-request track-sc" for a complete description. The only difference
@ -9036,6 +9052,7 @@ tcp-request content <action> [{if | unless} <condition>]
- sc-inc-gpc0(<sc-id>) - sc-inc-gpc0(<sc-id>)
- sc-set-gpt0(<sc-id>) <int> - sc-set-gpt0(<sc-id>) <int>
- set-var(<var-name>) <expr> - set-var(<var-name>) <expr>
- unset-var(<var-name>)
- silent-drop - silent-drop
They have the same meaning as their counter-parts in "tcp-request connection" They have the same meaning as their counter-parts in "tcp-request connection"
@ -9087,9 +9104,13 @@ tcp-request content <action> [{if | unless} <condition>]
<expr> Is a standard HAProxy expression formed by a sample-fetch <expr> Is a standard HAProxy expression formed by a sample-fetch
followed by some converters. followed by some converters.
The "unset-var" is used to unset a variable. See above for details about
<var-name>.
Example: Example:
tcp-request content set-var(sess.my_var) src tcp-request content set-var(sess.my_var) src
tcp-request content unset-var(sess.my_var2)
Example: Example:
# Accept HTTP requests containing a Host header saying "example.com" # Accept HTTP requests containing a Host header saying "example.com"
@ -9242,6 +9263,9 @@ tcp-response content <action> [{if | unless} <condition>]
- set-var(<var-name>) <expr> - set-var(<var-name>) <expr>
Sets a variable. Sets a variable.
- unset-var(<var-name>)
Unsets a variable.
- sc-inc-gpc0(<sc-id>): - sc-inc-gpc0(<sc-id>):
This action increments the GPC0 counter according to the sticky This action increments the GPC0 counter according to the sticky
counter designated by <sc-id>. If an error occurs, this action fails counter designated by <sc-id>. If an error occurs, this action fails
@ -9305,6 +9329,13 @@ tcp-response content <action> [{if | unless} <condition>]
tcp-request content set-var(sess.my_var) src tcp-request content set-var(sess.my_var) src
The "unset-var" is used to unset a variable. See above for details about
<var-name>.
Example:
tcp-request content unset-var(sess.my_var)
See section 7 about ACL usage. See section 7 about ACL usage.
See also : "tcp-request content", "tcp-response inspect-delay" See also : "tcp-request content", "tcp-response inspect-delay"
@ -9353,6 +9384,7 @@ tcp-request session <action> [{if | unless} <condition>]
- sc-inc-gpc0(<sc-id>) - sc-inc-gpc0(<sc-id>)
- sc-set-gpt0(<sc-id>) <int> - sc-set-gpt0(<sc-id>) <int>
- set-var(<var-name>) <expr> - set-var(<var-name>) <expr>
- unset-var(<var-name>)
- silent-drop - silent-drop
These actions have the same meaning as their respective counter-parts in These actions have the same meaning as their respective counter-parts in
@ -12683,6 +12715,18 @@ url_dec
Takes an url-encoded string provided as input and returns the decoded Takes an url-encoded string provided as input and returns the decoded
version as output. The input and the output are of type string. version as output. The input and the output are of type string.
unset-var(<var name>)
Unsets a variable if the input content is defined. The name of the variable
starts with an indication about its scope. The scopes allowed are:
"proc" : the variable is shared with the whole process
"sess" : the variable is shared with the whole session
"txn" : the variable is shared with the transaction (request and
response),
"req" : the variable is shared only during request processing,
"res" : the variable is shared only during response processing.
This prefix is followed by a name. The separator is a '.'. The name may only
contain characters 'a-z', 'A-Z', '0-9', '.' and '_'.
utime(<format>[,<offset>]) utime(<format>[,<offset>])
Converts an integer supposed to contain a date since epoch to a string Converts an integer supposed to contain a date since epoch to a string
representing this date in UTC time using a format defined by the <format> representing this date in UTC time using a format defined by the <format>

View File

@ -1419,7 +1419,13 @@ TXN class
:param class_txn txn: The class txn object containing the data. :param class_txn txn: The class txn object containing the data.
:param string var: The variable name according with the HAProxy variable syntax. :param string var: The variable name according with the HAProxy variable syntax.
:param opaque value: The data which is stored in the variable.
.. js:function:: TXN.unset_var(TXN, var)
Unset the variable <var>.
:param class_txn txn: The class txn object containing the data.
:param string var: The variable name according with the HAProxy variable syntax.
.. js:function:: TXN.get_var(TXN, var) .. js:function:: TXN.get_var(TXN, var)

View File

@ -9,6 +9,8 @@ void vars_prune_per_sess(struct vars *vars);
int vars_get_by_name(const char *name, size_t len, struct sample *smp); int vars_get_by_name(const char *name, size_t len, struct sample *smp);
void vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp); void vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp);
void vars_set_by_name(const char *name, size_t len, struct sample *smp); void vars_set_by_name(const char *name, size_t len, struct sample *smp);
void vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp);
void vars_unset_by_name(const char *name, size_t len, struct sample *smp);
int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp); int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp);
int vars_check_arg(struct arg *arg, char **err); int vars_check_arg(struct arg *arg, char **err);

View File

@ -4577,6 +4577,27 @@ __LJMP static int hlua_set_var(lua_State *L)
return 0; return 0;
} }
__LJMP static int hlua_unset_var(lua_State *L)
{
struct hlua_txn *htxn;
const char *name;
size_t len;
struct sample smp;
MAY_LJMP(check_args(L, 2, "unset_var"));
/* It is useles to retrieve the stream, but this function
* runs only in a stream context.
*/
htxn = MAY_LJMP(hlua_checktxn(L, 1));
name = MAY_LJMP(luaL_checklstring(L, 2, &len));
/* Unset the variable. */
smp_set_owner(&smp, htxn->p, htxn->s->sess, htxn->s, htxn->dir & SMP_OPT_DIR);
vars_unset_by_name(name, len, &smp);
return 0;
}
__LJMP static int hlua_get_var(lua_State *L) __LJMP static int hlua_get_var(lua_State *L)
{ {
struct hlua_txn *htxn; struct hlua_txn *htxn;
@ -6951,6 +6972,7 @@ void hlua_init(void)
hlua_class_function(gL.T, "set_priv", hlua_set_priv); hlua_class_function(gL.T, "set_priv", hlua_set_priv);
hlua_class_function(gL.T, "get_priv", hlua_get_priv); hlua_class_function(gL.T, "get_priv", hlua_get_priv);
hlua_class_function(gL.T, "set_var", hlua_set_var); hlua_class_function(gL.T, "set_var", hlua_set_var);
hlua_class_function(gL.T, "unset_var", hlua_unset_var);
hlua_class_function(gL.T, "get_var", hlua_get_var); hlua_class_function(gL.T, "get_var", hlua_get_var);
hlua_class_function(gL.T, "done", hlua_txn_done); hlua_class_function(gL.T, "done", hlua_txn_done);
hlua_class_function(gL.T, "set_loglevel",hlua_txn_set_loglevel); hlua_class_function(gL.T, "set_loglevel",hlua_txn_set_loglevel);

View File

@ -86,6 +86,25 @@ static int var_accounting_add(struct vars *vars, struct session *sess, struct st
return 1; return 1;
} }
/* This fnuction remove a variable from the list and free memory it used */
unsigned int var_clear(struct var *var)
{
unsigned int size = 0;
if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
free(var->data.u.str.str);
size += var->data.u.str.len;
}
else if (var->data.type == SMP_T_METH) {
free(var->data.u.meth.str.str);
size += var->data.u.meth.str.len;
}
LIST_DEL(&var->l);
pool_free2(var_pool, var);
size += sizeof(struct var);
return size;
}
/* This function free all the memory used by all the varaibles /* This function free all the memory used by all the varaibles
* in the list. * in the list.
*/ */
@ -95,18 +114,7 @@ void vars_prune(struct vars *vars, struct session *sess, struct stream *strm)
unsigned int size = 0; unsigned int size = 0;
list_for_each_entry_safe(var, tmp, &vars->head, l) { list_for_each_entry_safe(var, tmp, &vars->head, l) {
if (var->data.type == SMP_T_STR || size += var_clear(var);
var->data.type == SMP_T_BIN) {
free(var->data.u.str.str);
size += var->data.u.str.len;
}
else if (var->data.type == SMP_T_METH) {
free(var->data.u.meth.str.str);
size += var->data.u.meth.str.len;
}
LIST_DEL(&var->l);
pool_free2(var_pool, var);
size += sizeof(struct var);
} }
var_accounting_diff(vars, sess, strm, -size); var_accounting_diff(vars, sess, strm, -size);
} }
@ -120,18 +128,7 @@ void vars_prune_per_sess(struct vars *vars)
unsigned int size = 0; unsigned int size = 0;
list_for_each_entry_safe(var, tmp, &vars->head, l) { list_for_each_entry_safe(var, tmp, &vars->head, l) {
if (var->data.type == SMP_T_STR || size += var_clear(var);
var->data.type == SMP_T_BIN) {
free(var->data.u.str.str);
size += var->data.u.str.len;
}
else if (var->data.type == SMP_T_METH) {
free(var->data.u.meth.str.str);
size += var->data.u.meth.str.len;
}
LIST_DEL(&var->l);
pool_free2(var_pool, var);
size += sizeof(struct var);
} }
vars->size -= size; vars->size -= size;
global.vars.size -= size; global.vars.size -= size;
@ -398,12 +395,45 @@ static inline int sample_store_stream(const char *name, enum vars_scope scope, s
return sample_store(vars, name, smp); return sample_store(vars, name, smp);
} }
/* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */
static inline int sample_clear_stream(const char *name, enum vars_scope scope, struct sample *smp)
{
struct vars *vars;
struct var *var;
unsigned int size = 0;
switch (scope) {
case SCOPE_PROC: vars = &global.vars; break;
case SCOPE_SESS: vars = &smp->sess->vars; break;
case SCOPE_TXN: vars = &smp->strm->vars_txn; break;
case SCOPE_REQ:
case SCOPE_RES:
default: vars = &smp->strm->vars_reqres; break;
}
if (vars->scope != scope)
return 0;
/* Look for existing variable name. */
var = var_get(vars, name);
if (var) {
size = var_clear(var);
var_accounting_diff(vars, smp->sess, smp->strm, -size);
}
return 1;
}
/* Returns 0 if fails, else returns 1. */ /* Returns 0 if fails, else returns 1. */
static int smp_conv_store(const struct arg *args, struct sample *smp, void *private) static int smp_conv_store(const struct arg *args, struct sample *smp, void *private)
{ {
return sample_store_stream(args[0].data.var.name, args[0].data.var.scope, smp); return sample_store_stream(args[0].data.var.name, args[0].data.var.scope, smp);
} }
/* Returns 0 if fails, else returns 1. */
static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private)
{
return sample_clear_stream(args[0].data.var.name, args[0].data.var.scope, smp);
}
/* This fucntions check an argument entry and fill it with a variable /* This fucntions check an argument entry and fill it with a variable
* type. The argumen must be a string. If the variable lookup fails, * type. The argumen must be a string. If the variable lookup fails,
* the function retuns 0 and fill <err>, otherwise it returns 1. * the function retuns 0 and fill <err>, otherwise it returns 1.
@ -462,6 +492,37 @@ void vars_set_by_name(const char *name, size_t len, struct sample *smp)
sample_store_stream(name, scope, smp); sample_store_stream(name, scope, smp);
} }
/* This function unset a variable if it was already defined.
* In error case, it fails silently.
*/
void vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp)
{
enum vars_scope scope;
/* Resolve name and scope. */
name = register_name(name, len, &scope, 0, NULL);
if (!name)
return;
sample_clear_stream(name, scope, smp);
}
/* This function unset a variable.
* In error case, it fails silently.
*/
void vars_unset_by_name(const char *name, size_t len, struct sample *smp)
{
enum vars_scope scope;
/* Resolve name and scope. */
name = register_name(name, len, &scope, 1, NULL);
if (!name)
return;
sample_clear_stream(name, scope, smp);
}
/* this function fills a sample with the /* this function fills a sample with the
* variable content. Returns 1 if the sample * variable content. Returns 1 if the sample
* is filled, otherwise it returns 0. * is filled, otherwise it returns 0.
@ -567,6 +628,20 @@ static enum act_return action_store(struct act_rule *rule, struct proxy *px,
return ACT_RET_CONT; return ACT_RET_CONT;
} }
/* Always returns ACT_RET_CONT even if an error occurs. */
static enum act_return action_clear(struct act_rule *rule, struct proxy *px,
struct session *sess, struct stream *s, int flags)
{
struct sample smp;
memset(&smp, 0, sizeof(smp));
smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL);
/* Clear the variable using the sample context, and ignore errors. */
sample_clear_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
return ACT_RET_CONT;
}
/* This two function checks the variable name and replace the /* This two function checks the variable name and replace the
* configuration string name by the global string name. its * configuration string name by the global string name. its
* the same string, but the global pointer can be easy to * the same string, but the global pointer can be easy to
@ -601,18 +676,28 @@ static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy
const char *var_name = args[*arg-1]; const char *var_name = args[*arg-1];
int var_len; int var_len;
const char *kw_name; const char *kw_name;
int flags; int flags, set_var;
if (!strncmp(var_name, "set-var", 7)) {
var_name += 7;
set_var = 1;
}
if (!strncmp(var_name, "unset-var", 9)) {
var_name += 9;
set_var = 0;
}
var_name += strlen("set-var");
if (*var_name != '(') { if (*var_name != '(') {
memprintf(err, "invalid variable '%s'. Expects 'set-var(<var-name>)'", args[*arg-1]); memprintf(err, "invalid variable '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
args[*arg-1]);
return ACT_RET_PRS_ERR; return ACT_RET_PRS_ERR;
} }
var_name++; /* jump the '(' */ var_name++; /* jump the '(' */
var_len = strlen(var_name); var_len = strlen(var_name);
var_len--; /* remove the ')' */ var_len--; /* remove the ')' */
if (var_name[var_len] != ')') { if (var_name[var_len] != ')') {
memprintf(err, "invalid variable '%s'. Expects 'set-var(<var-name>)'", args[*arg-1]); memprintf(err, "invalid variable '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
args[*arg-1]);
return ACT_RET_PRS_ERR; return ACT_RET_PRS_ERR;
} }
@ -620,6 +705,18 @@ static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy
if (!rule->arg.vars.name) if (!rule->arg.vars.name)
return ACT_RET_PRS_ERR; return ACT_RET_PRS_ERR;
/* There is no fetch method when variable is unset. Just set the right
* action and return. */
if (!set_var) {
if (*args[*arg]) {
memprintf(err, "fetch method not supported");
return ACT_RET_PRS_ERR;
}
rule->action = ACT_CUSTOM;
rule->action_ptr = action_clear;
return ACT_RET_PRS_OK;
}
kw_name = args[*arg-1]; kw_name = args[*arg-1];
rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file, rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
@ -708,32 +805,38 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
}}; }};
static struct sample_conv_kw_list sample_conv_kws = {ILH, { static struct sample_conv_kw_list sample_conv_kws = {ILH, {
{ "set-var", smp_conv_store, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY }, { "set-var", smp_conv_store, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
{ "unset-var", smp_conv_clear, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
{ /* END */ }, { /* END */ },
}}; }};
static struct action_kw_list tcp_req_sess_kws = { { }, { static struct action_kw_list tcp_req_sess_kws = { { }, {
{ "set-var", parse_store, 1 }, { "set-var", parse_store, 1 },
{ "unset-var", parse_store, 1 },
{ /* END */ } { /* END */ }
}}; }};
static struct action_kw_list tcp_req_cont_kws = { { }, { static struct action_kw_list tcp_req_cont_kws = { { }, {
{ "set-var", parse_store, 1 }, { "set-var", parse_store, 1 },
{ "unset-var", parse_store, 1 },
{ /* END */ } { /* END */ }
}}; }};
static struct action_kw_list tcp_res_kws = { { }, { static struct action_kw_list tcp_res_kws = { { }, {
{ "set-var", parse_store, 1 }, { "set-var", parse_store, 1 },
{ "unset-var", parse_store, 1 },
{ /* END */ } { /* END */ }
}}; }};
static struct action_kw_list http_req_kws = { { }, { static struct action_kw_list http_req_kws = { { }, {
{ "set-var", parse_store, 1 }, { "set-var", parse_store, 1 },
{ "unset-var", parse_store, 1 },
{ /* END */ } { /* END */ }
}}; }};
static struct action_kw_list http_res_kws = { { }, { static struct action_kw_list http_res_kws = { { }, {
{ "set-var", parse_store, 1 }, { "set-var", parse_store, 1 },
{ "unset-var", parse_store, 1 },
{ /* END */ } { /* END */ }
}}; }};