]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: vars: Add 'unset-var' action/converter
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 9 Nov 2016 15:54:56 +0000 (16:54 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 9 Nov 2016 21:57:01 +0000 (22:57 +0100)
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.

doc/configuration.txt
doc/lua-api/index.rst
include/proto/vars.h
src/hlua.c
src/vars.c

index a240017e488c6febc43ac864883ff0fcee06fd66..2a6183cfc9b78b137753a489037ebdcb55825554 100644 (file)
@@ -3655,6 +3655,7 @@ http-request { allow | tarpit | auth [realm <realm>] | redirect <rule> |
               del-map(<file name>) <key fmt> |
               set-map(<file name>) <key fmt> <value fmt> |
               set-var(<var name>) <expr> |
+              unset-var(<var name>) |
               { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] |
               sc-inc-gpc0(<sc-id>) |
               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
 
+    - 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> :
       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 <name> <fmt> | set-nice <nice> |
                 del-map(<file name>) <key fmt> |
                 set-map(<file name>) <key fmt> <value fmt> |
                 set-var(<var-name>) <expr> |
+                unset-var(<var-name>) |
                 { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] |
                 sc-inc-gpc0(<sc-id>) |
                 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)
 
+    - 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>] :
       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 <action> [{if | unless} <condition>]
     - sc-inc-gpc0(<sc-id>)
     - sc-set-gpt0(<sc-id>) <int>
     - set-var(<var-name>) <expr>
+    - unset-var(<var-name>)
     - silent-drop
 
   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
                followed by some converters.
 
+  The "unset-var" is used to unset a variable. See above for details about
+  <var-name>.
+
   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 <action> [{if | unless} <condition>]
     - set-var(<var-name>) <expr>
         Sets a variable.
 
+    - unset-var(<var-name>)
+        Unsets a variable.
+
     - sc-inc-gpc0(<sc-id>):
         This action increments the GPC0 counter according to the sticky
         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
 
+  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 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-set-gpt0(<sc-id>) <int>
     - set-var(<var-name>) <expr>
+    - unset-var(<var-name>)
     - 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(<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>])
   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>
index 9e1b530a9d6d1d8899ed22a63c159090b434c907..c42b2f3085a2d805ee36e7716324614bd8f2fdb5 100644 (file)
@@ -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 <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)
 
index 8414baac12d6f06282e846b64d18fc2a7b12c34b..6152f5c8b2875a571980aa194a8404b2585a0683 100644 (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);
 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);
 
index 9d81634e1f1561e0df1f926c2c14952b7ab1f73c..121c283f1c25bfdbaefee614a6a4d8c0aae4b9e7 100644 (file)
@@ -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);
index dcdf1b563682f0f38769da25c69c588ac3aa2a3a..29ac3350bfd86f553a515f7b0eeaf74b06c23a09 100644 (file)
@@ -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 <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);
 }
 
+/* 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(<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;
        }
        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(<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;
        }
 
@@ -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 */ }
 }};