[Sorry for multiple posting. I'm having trouble with sending this
patch via GMail *SMTP*; trying the WUI...]

Thanks Willy for your feedback.

I'm not sure whether I should further split stktable_fetch_expr() into
some new sample_...() in sample.c or if there is some other *.c file where
a generic function, including the required act_rule, would make sense.

Also, I'm not HAProxy-savvy enough to know whether this proposal is
multi-thread safe (is it ?).

[patch]
Allow the sc-set-gpt0 action to set GPT0 to a value dynamically evaluated from
its <expr> argument (in addition to the existing static <int> alternative).

---
 doc/configuration.txt  | 44 ++++++++++++-----------
 include/types/action.h |  1 +
 src/stick_table.c      | 82 +++++++++++++++++++++++++++++++++++++++---
 3 files changed, 102 insertions(+), 25 deletions(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 8dedbfc48..bea62bd98 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4465,11 +4465,13 @@ http-request sc-inc-gpc1(<sc-id>) [ { if |
unless } <condition> ]
   counter designated by <sc-id>. If an error occurs, this action silently fails
   and the actions evaluation continues.

-http-request sc-set-gpt0(<sc-id>) <int> [ { if | unless } <condition> ]
+http-request sc-set-gpt0(<sc-id>) { <int> | <expr> }
+                                  [ { if | unless } <condition> ]

-  This action sets the GPT0 tag according to the sticky counter designated by
-  <sc-id> and the value of <int>. The expected result is a boolean. If an error
-  occurs, this action silently fails and the actions evaluation continues.
+  This action sets the 32-bit unsigned GPT0 tag according to the sticky counter
+  designated by <sc-id> and the value of <int>/<expr>. The expected result is a
+  boolean. If an error occurs, this action silently fails and the actions
+  evaluation continues.

 http-request set-dst <expr> [ { if | unless } <condition> ]

@@ -4974,11 +4976,13 @@ http-response sc-inc-gpc1(<sc-id>) [ { if |
unless } <condition> ]
   counter designated by <sc-id>. If an error occurs, this action silently fails
   and the actions evaluation continues.

-http-response sc-set-gpt0(<sc-id>) <int> [ { if | unless } <condition> ]
+http-response sc-set-gpt0(<sc-id>) { <int> | <expr> }
+                                   [ { if | unless } <condition> ]

-  This action sets the GPT0 tag according to the sticky counter designated by
-  <sc-id> and the value of <int>. The expected result is a boolean. If an error
-  occurs, this action silently fails and the actions evaluation continues.
+  This action sets the 32-bit unsigned GPT0 tag according to the sticky counter
+  designated by <sc-id> and the value of <int>/<expr>. The expected result is a
+  boolean. If an error occurs, this action silently fails and the actions
+  evaluation continues.

 http-response send-spoe-group [ { if | unless } <condition> ]

@@ -9394,11 +9398,11 @@ tcp-request connection <action> [{if | unless}
<condition>]
         counter designated by <sc-id>. If an error occurs, this action silently
         fails and the actions evaluation continues.

-    - sc-set-gpt0(<sc-id>) <int>:
-        This action sets the GPT0 tag according to the sticky counter
designated
-        by <sc-id> and the value of <int>. The expected result is a boolean. If
-        an error occurs, this action silently fails and the actions evaluation
-        continues.
+    - sc-set-gpt0(<sc-id>) { <int> | <expr> }:
+        This action sets the 32-bit unsigned GPT0 tag according to the sticky
+        counter designated by <sc-id> and the value of <int>/<expr>. The
+        expected result is a boolean. If an error occurs, this action silently
+        fails and the actions evaluation continues.

     - set-src <expr> :
       Is used to set the source IP address to the value of specified
@@ -9556,7 +9560,7 @@ tcp-request content <action> [{if | unless} <condition>]
     - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>]
     - sc-inc-gpc0(<sc-id>)
     - sc-inc-gpc1(<sc-id>)
-    - sc-set-gpt0(<sc-id>) <int>
+    - sc-set-gpt0(<sc-id>) { <int> | <expr> }
     - set-dst <expr>
     - set-dst-port <expr>
     - set-var(<var-name>) <expr>
@@ -9820,11 +9824,11 @@ tcp-response content <action> [{if | unless}
<condition>]
         counter designated by <sc-id>. If an error occurs, this action fails
         silently and the actions evaluation continues.

-    - sc-set-gpt0(<sc-id>) <int> :
-        This action sets the GPT0 tag according to the sticky counter
designated
-        by <sc-id> and the value of <int>. The expected result is a boolean. If
-        an error occurs, this action silently fails and the actions evaluation
-        continues.
+    - sc-set-gpt0(<sc-id>) { <int> | <expr> }
+        This action sets the 32-bit unsigned GPT0 tag according to the sticky
+        counter designated by <sc-id> and the value of <int>/<expr>. The
+        expected result is a boolean. If an error occurs, this action silently
+        fails and the actions evaluation continues.

     - "silent-drop" :
         This stops the evaluation of the rules and makes the client-facing
@@ -9945,7 +9949,7 @@ tcp-request session <action> [{if | unless} <condition>]
     - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>]
     - sc-inc-gpc0(<sc-id>)
     - sc-inc-gpc1(<sc-id>)
-    - sc-set-gpt0(<sc-id>) <int>
+    - sc-set-gpt0(<sc-id>) { <int> | <expr> }
     - set-var(<var-name>) <expr>
     - unset-var(<var-name>)
     - silent-drop
diff --git a/include/types/action.h b/include/types/action.h
index 54a6f71a4..516ffddd0 100644
--- a/include/types/action.h
+++ b/include/types/action.h
@@ -168,6 +168,7 @@ struct act_rule {
                struct {
                        int sc;
                        long long int value;
+                       struct sample_expr *expr;
                } gpt;
                struct track_ctr_prm trk_ctr;
                struct {
diff --git a/src/stick_table.c b/src/stick_table.c
index c9f3e0636..cd0cbe862 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -1845,6 +1845,48 @@ static int sample_conv_table_trackers(const
struct arg *arg_p, struct sample *sm
        return 1;
 }

+/* Fetches and hydrates the sample data from the given expression.
+ * Returns 1 on success, 0 otherwise.
+ */
+static int stktable_fetch_expr(struct act_rule *rule, struct proxy *px,
+                               struct session *sess, struct stream *s,
+                               struct sample_expr *expr,
+                               int smp_type, struct sample *smp)
+{
+       int smp_opt_dir;
+
+       switch (rule->from) {
+       case ACT_F_TCP_REQ_SES: smp_opt_dir = SMP_OPT_DIR_REQ; break;
+       case ACT_F_TCP_REQ_CNT: smp_opt_dir = SMP_OPT_DIR_REQ; break;
+       case ACT_F_TCP_RES_CNT: smp_opt_dir = SMP_OPT_DIR_RES; break;
+       case ACT_F_HTTP_REQ:    smp_opt_dir = SMP_OPT_DIR_REQ; break;
+       case ACT_F_HTTP_RES:    smp_opt_dir = SMP_OPT_DIR_RES; break;
+       default:
+               send_log(px, LOG_ERR, "stick table: internal error
while fetching expression.");
+               if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
+                       ha_alert("stick table: internal error while
executing fetching expression.\n");
+               return 0;
+       }
+
+       /* Process the expression. */
+       memset(smp, 0, sizeof(*smp));
+       if (!sample_process(px, sess, s, smp_opt_dir|SMP_OPT_FINAL, expr, smp))
+               return 0;
+
+       /* Check the sample data type. */
+       if (!sample_casts[smp->data.type][smp_type] ||
+           !sample_casts[smp->data.type][smp_type](smp)) {
+               send_log(px, LOG_WARNING, "stick table: invalid data
type while fetching expression.");
+               if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
+                       ha_alert("stick table: invalid data type while
fetching expression.\n");
+               return 0;
+       }
+
+       /* OK cast succeeded. */
+
+       return 1;
+}
+
 /* Always returns 1. */
 static enum act_return action_inc_gpc0(struct act_rule *rule, struct proxy *px,
                                        struct session *sess, struct
stream *s, int flags)
@@ -2016,6 +2058,8 @@ static enum act_return action_set_gpt0(struct
act_rule *rule, struct proxy *px,
        void *ptr;
        struct stksess *ts;
        struct stkctr *stkctr;
+       unsigned int value = 0;
+       struct sample smp;

        /* Extract the stksess, return OK if no stksess available. */
        if (s)
@@ -2030,9 +2074,17 @@ static enum act_return action_set_gpt0(struct
act_rule *rule, struct proxy *px,
        /* Store the sample in the required sc, and ignore errors. */
        ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_GPT0);
        if (ptr) {
+               if (!rule->arg.gpt.expr)
+                       value = (unsigned int)(rule->arg.gpt.value);
+               else {
+                       if (!stktable_fetch_expr(rule, px, sess, s,
rule->arg.gpt.expr, SMP_T_SINT, &smp))
+                               return ACT_RET_CONT;
+                       value = (unsigned int)(smp.data.u.sint);
+               }
+
                HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);

-               stktable_data_cast(ptr, gpt0) = rule->arg.gpt.value;
+               stktable_data_cast(ptr, gpt0) = value;

                HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);

@@ -2058,6 +2110,7 @@ static enum act_parse_ret parse_set_gpt0(const
char **args, int *arg, struct pro
 {
        const char *cmd_name = args[*arg-1];
        char *error;
+       int smp_val;

        cmd_name += strlen("sc-set-gpt0");
        if (*cmd_name == '\0') {
@@ -2077,16 +2130,35 @@ static enum act_parse_ret parse_set_gpt0(const
char **args, int *arg, struct pro
                }

                if (rule->arg.gpt.sc >= ACT_ACTION_TRK_SCMAX) {
-                       memprintf(err, "invalid stick table track ID
'%s'. The max allowed ID is %d",
-                                 args[*arg-1], ACT_ACTION_TRK_SCMAX-1);
+                       memprintf(err, "invalid stick table track ID
'%s'. The max allowed ID is %d", args[*arg-1],
ACT_ACTION_TRK_SCMAX-1);
                        return ACT_RET_PRS_ERR;
                }
        }

+       rule->arg.gpt.expr = NULL;
        rule->arg.gpt.value = strtol(args[*arg], &error, 10);
        if (*error != '\0') {
-               memprintf(err, "invalid integer value '%s'", args[*arg]);
-               return ACT_RET_PRS_ERR;
+               rule->arg.gpt.expr = sample_parse_expr((char **)args,
arg, px->conf.args.file,
+
px->conf.args.line, err, &px->conf.args);
+               if (!rule->arg.gpt.expr)
+                       return ACT_RET_PRS_ERR;
+
+               switch (rule->from) {
+               case ACT_F_TCP_REQ_SES: smp_val = SMP_VAL_FE_SES_ACC; break;
+               case ACT_F_TCP_REQ_CNT: smp_val = SMP_VAL_FE_REQ_CNT; break;
+               case ACT_F_TCP_RES_CNT: smp_val = SMP_VAL_BE_RES_CNT; break;
+               case ACT_F_HTTP_REQ:    smp_val = SMP_VAL_FE_HRQ_HDR; break;
+               case ACT_F_HTTP_RES:    smp_val = SMP_VAL_BE_HRS_HDR; break;
+               default:
+                       memprintf(err, "internal error, unexpected
rule->from=%d, please report this bug!", rule->from);
+                       return ACT_RET_PRS_ERR;
+               }
+               if (!(rule->arg.gpt.expr->fetch->val & smp_val)) {
+                       memprintf(err, "fetch method '%s' extracts
information from '%s', none of which is available here",
+                                 args[*arg-1],
sample_src_names(rule->arg.gpt.expr->fetch->use));
+                       free(rule->arg.gpt.expr);
+                       return ACT_RET_PRS_ERR;
+               }
        }
        (*arg)++;

--
2.20.1

Reply via email to