diff --git a/doc/configuration.txt b/doc/configuration.txt index a240017e4..2a6183cfc 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3655,6 +3655,7 @@ http-request { allow | tarpit | auth [realm ] | redirect | del-map() | set-map() | set-var() | + unset-var() | { track-sc0 | track-sc1 | track-sc2 } [table ] | sc-inc-gpc0() | sc-set-gpt0() | @@ -3962,6 +3963,13 @@ http-request { allow | tarpit | auth [realm ] | redirect | http-request set-var(req.my_var) req.fhdr(user-agent),lower + - unset-var() : + Is used to unset a variable. See above for details about . + + Example: + + http-request unset-var(req.my_var) + - set-src : 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, @@ -4119,6 +4127,7 @@ http-response { allow | deny | add-header | set-nice | del-map() | set-map() | set-var() | + unset-var() | { track-sc0 | track-sc1 | track-sc2 } [table
] | sc-inc-gpc0() | sc-set-gpt0() | @@ -4328,6 +4337,13 @@ http-response { allow | deny | add-header | set-nice | http-response set-var(sess.last_redir) res.hdr(location) + - unset-var() : + Is used to unset a variable. See above for details about . + + Example: + + http-response unset-var(sess.last_redir) + - { track-sc0 | track-sc1 | track-sc2 } [table
] : enables tracking of sticky counters from current response. Please refer to "http-request track-sc" for a complete description. The only difference @@ -9036,6 +9052,7 @@ tcp-request content [{if | unless} ] - sc-inc-gpc0() - sc-set-gpt0() - set-var() + - unset-var() - silent-drop They have the same meaning as their counter-parts in "tcp-request connection" @@ -9087,9 +9104,13 @@ tcp-request content [{if | unless} ] Is a standard HAProxy expression formed by a sample-fetch followed by some converters. + The "unset-var" is used to unset a variable. See above for details about + . + Example: tcp-request content set-var(sess.my_var) src + tcp-request content unset-var(sess.my_var2) Example: # Accept HTTP requests containing a Host header saying "example.com" @@ -9242,6 +9263,9 @@ tcp-response content [{if | unless} ] - set-var() Sets a variable. + - unset-var() + Unsets a variable. + - sc-inc-gpc0(): This action increments the GPC0 counter according to the sticky counter designated by . If an error occurs, this action fails @@ -9305,6 +9329,13 @@ tcp-response content [{if | unless} ] tcp-request content set-var(sess.my_var) src + The "unset-var" is used to unset a variable. See above for details about + . + + Example: + + tcp-request content unset-var(sess.my_var) + See section 7 about ACL usage. See also : "tcp-request content", "tcp-response inspect-delay" @@ -9353,6 +9384,7 @@ tcp-request session [{if | unless} ] - sc-inc-gpc0() - sc-set-gpt0() - set-var() + - unset-var() - silent-drop 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 version as output. The input and the output are of type string. +unset-var() + 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([,]) 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 diff --git a/doc/lua-api/index.rst b/doc/lua-api/index.rst index 9e1b530a9..c42b2f308 100644 --- a/doc/lua-api/index.rst +++ b/doc/lua-api/index.rst @@ -1419,7 +1419,13 @@ TXN class :param class_txn txn: The class txn object containing the data. :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 . + + :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) diff --git a/include/proto/vars.h b/include/proto/vars.h index 8414baac1..6152f5c8b 100644 --- a/include/proto/vars.h +++ b/include/proto/vars.h @@ -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); 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_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_check_arg(struct arg *arg, char **err); diff --git a/src/hlua.c b/src/hlua.c index 9d81634e1..121c283f1 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -4577,6 +4577,27 @@ __LJMP static int hlua_set_var(lua_State *L) 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) { 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, "get_priv", hlua_get_priv); 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, "done", hlua_txn_done); hlua_class_function(gL.T, "set_loglevel",hlua_txn_set_loglevel); diff --git a/src/vars.c b/src/vars.c index dcdf1b563..29ac3350b 100644 --- a/src/vars.c +++ b/src/vars.c @@ -86,6 +86,25 @@ static int var_accounting_add(struct vars *vars, struct session *sess, struct st 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 * in the list. */ @@ -95,18 +114,7 @@ void vars_prune(struct vars *vars, struct session *sess, struct stream *strm) unsigned int size = 0; list_for_each_entry_safe(var, tmp, &vars->head, l) { - 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); + size += var_clear(var); } var_accounting_diff(vars, sess, strm, -size); } @@ -120,18 +128,7 @@ void vars_prune_per_sess(struct vars *vars) unsigned int size = 0; list_for_each_entry_safe(var, tmp, &vars->head, l) { - 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); + size += var_clear(var); } 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); } +/* 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. */ 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); } +/* 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 * type. The argumen must be a string. If the variable lookup fails, * the function retuns 0 and fill , 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); } +/* 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 * variable content. Returns 1 if the sample * 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; } +/* 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 * configuration string name by the global string name. its * 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]; int var_len; 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 != '(') { - memprintf(err, "invalid variable '%s'. Expects 'set-var()'", args[*arg-1]); + memprintf(err, "invalid variable '%s'. Expects 'set-var()' or 'unset-var()'", + args[*arg-1]); return ACT_RET_PRS_ERR; } var_name++; /* jump the '(' */ var_len = strlen(var_name); var_len--; /* remove the ')' */ if (var_name[var_len] != ')') { - memprintf(err, "invalid variable '%s'. Expects 'set-var()'", args[*arg-1]); + memprintf(err, "invalid variable '%s'. Expects 'set-var()' or 'unset-var()'", + args[*arg-1]); 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) 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]; 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, { - { "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 */ }, }}; 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 */ } }}; 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 */ } }}; static struct action_kw_list tcp_res_kws = { { }, { - { "set-var", parse_store, 1 }, + { "set-var", parse_store, 1 }, + { "unset-var", parse_store, 1 }, { /* END */ } }}; static struct action_kw_list http_req_kws = { { }, { - { "set-var", parse_store, 1 }, + { "set-var", parse_store, 1 }, + { "unset-var", parse_store, 1 }, { /* END */ } }}; static struct action_kw_list http_res_kws = { { }, { - { "set-var", parse_store, 1 }, + { "set-var", parse_store, 1 }, + { "unset-var", parse_store, 1 }, { /* END */ } }};