MINOR: stick-table: allow sc-set-gpt0 to set value from an expression

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).
diff --git a/doc/configuration.txt b/doc/configuration.txt
index a4841fc..7ef5bdf 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4471,11 +4471,13 @@
   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> ]
 
@@ -4980,11 +4982,13 @@
   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> ]
 
@@ -9398,11 +9402,11 @@
         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
@@ -9560,7 +9564,7 @@
     - { 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>
@@ -9824,11 +9828,11 @@
         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
@@ -9949,7 +9953,7 @@
     - { 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 54a6f71..516ffdd 100644
--- a/include/types/action.h
+++ b/include/types/action.h
@@ -168,6 +168,7 @@
 		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 c9f3e06..1b70b46 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -2016,6 +2016,9 @@
 	void *ptr;
 	struct stksess *ts;
 	struct stkctr *stkctr;
+	unsigned int value = 0;
+	struct sample *smp;
+	int smp_opt_dir;
 
 	/* Extract the stksess, return OK if no stksess available. */
 	if (s)
@@ -2030,9 +2033,36 @@
 	/* 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 {
+			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 setting gpt0.");
+				if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
+					ha_alert("stick table: internal error while executing setting gpt0.\n");
+				return ACT_RET_CONT;
+			}
+
+			/* Fetch and cast the expression. */
+			smp = sample_fetch_as_type(px, sess, s, smp_opt_dir|SMP_OPT_FINAL, rule->arg.gpt.expr, SMP_T_SINT);
+			if (!smp) {
+				send_log(px, LOG_WARNING, "stick table: invalid expression or data type while setting gpt0.");
+				if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
+					ha_alert("stick table: invalid expression or data type while setting gpt0.\n");
+				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 +2088,7 @@
 {
 	const char *cmd_name = args[*arg-1];
 	char *error;
+	int smp_val;
 
 	cmd_name += strlen("sc-set-gpt0");
 	if (*cmd_name == '\0') {
@@ -2083,10 +2114,30 @@
 		}
 	}
 
+	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)++;