MEDIUM: capture: Allow capture with slot identifier

This patch modifies the current http-request capture function
and adds a new keyword "id" that permits to identify a capture slot.
If the identified doesn't exists, the action fails silently.

Note that this patch removs an unused list initilisation, which seems
to be inherited from a copy/paste. It's harmless and does not need to
be backported.

   LIST_INIT((struct list *)&rule->arg.act.p[0]);
diff --git a/doc/configuration.txt b/doc/configuration.txt
index e0a59c6..fe03908 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -3278,7 +3278,7 @@
 
 http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
               add-header <name> <fmt> | set-header <name> <fmt> |
-              capture <sample> len <length> |
+              capture <sample> [ len <length> | id <id> ] |
               del-header <name> | set-nice <nice> | set-log-level <level> |
               replace-header <name> <match-regex> <replace-fmt> |
               replace-value <name> <match-regex> <replace-fmt> |
@@ -3497,7 +3497,7 @@
       with large lists! It is the equivalent of the "set map" command from the
       stats socket, but can be triggered by an HTTP request.
 
-    - capture <sample> len <length> :
+    - capture <sample> [ len <length> | id <id> ] :
       captures sample expression <sample> from the request buffer, and converts
       it to a string of at most <len> characters. The resulting string is
       stored into the next request "capture" slot, so it will possibly appear
@@ -3508,6 +3508,11 @@
       session life. Please check section 7.3 (Fetching samples) and "capture
       request header" for more information.
 
+      If the keyword "id" is used instead of "len", the action tries to store
+      the captured string in a previously declared capture slot. This is useful
+      to run captures in backends. The slot id can be declared by a previous
+      directive "http-request capture" or with the "declare capture" keyword.
+
     - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] :
       enables tracking of sticky counters from current request. These rules
       do not stop evaluation and do not change default action. Three sets of
diff --git a/src/proto_http.c b/src/proto_http.c
index 7a215c6..0f46fb0 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -12464,9 +12464,52 @@
 	return 1;
 }
 
+/* This function executes the "capture" action and store the result in a
+ * capture slot if exists. It executes a fetch expression, turns the result
+ * into a string and puts it in a capture slot. It always returns 1. If an
+ * error occurs the action is cancelled, but the rule processing continues.
+ */
+int http_action_req_capture_by_id(struct http_req_rule *rule, struct proxy *px, struct stream *s)
+{
+	struct session *sess = s->sess;
+	struct sample *key;
+	struct sample_expr *expr = rule->arg.act.p[0];
+	struct cap_hdr *h;
+	int idx = (long)rule->arg.act.p[1];
+	char **cap = s->req_cap;
+	struct proxy *fe = strm_fe(s);
+	int len;
+	int i;
+
+	/* Look for the original configuration. */
+	for (h = fe->req_cap, i = fe->nb_req_cap - 1;
+	     h != NULL && i != idx ;
+	     i--, h = h->next);
+	if (!h)
+		return 1;
+
+	key = sample_fetch_string(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, expr);
+	if (!key)
+		return 1;
+
+	if (cap[h->index] == NULL)
+		cap[h->index] = pool_alloc2(h->pool);
+
+	if (cap[h->index] == NULL) /* no more capture memory */
+		return 1;
+
+	len = key->data.str.len;
+	if (len > h->len)
+		len = h->len;
+
+	memcpy(cap[h->index], key->data.str.str, len);
+	cap[h->index][len] = 0;
+	return 1;
+}
+
 /* parse an "http-request capture" action. It takes a single argument which is
  * a sample fetch expression. It stores the expression into arg->act.p[0] and
- * the allocated hdr_cap struct into arg->act.p[1].
+ * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
  * It returns 0 on success, < 0 on error.
  */
 int parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px, struct http_req_rule *rule, char **err)
@@ -12482,18 +12525,10 @@
 			break;
 
 	if (cur_arg < *orig_arg + 3) {
-		memprintf(err, "expects <expression> 'len' <length> ");
-		return -1;
-	}
-
-	if (!(px->cap & PR_CAP_FE)) {
-		memprintf(err, "proxy '%s' has no frontend capability", px->id);
+		memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
 		return -1;
 	}
 
-	LIST_INIT((struct list *)&rule->arg.act.p[0]);
-	proxy->conf.args.ctx = ARGC_CAP;
-
 	cur_arg = *orig_arg;
 	expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args);
 	if (!expr)
@@ -12507,8 +12542,22 @@
 		return -1;
 	}
 
+	if (!args[cur_arg] || !*args[cur_arg]) {
+		memprintf(err, "expects 'len or 'id'");
+		free(expr);
+		return -1;
+	}
+
 	if (strcmp(args[cur_arg], "len") == 0) {
 		cur_arg++;
+
+		if (!(px->cap & PR_CAP_FE)) {
+			memprintf(err, "proxy '%s' has no frontend capability", px->id);
+			return -1;
+		}
+
+		proxy->conf.args.ctx = ARGC_CAP;
+
 		if (!args[cur_arg]) {
 			memprintf(err, "missing length value");
 			free(expr);
@@ -12522,30 +12571,63 @@
 			return -1;
 		}
 		cur_arg++;
-	}
 
-	if (!len) {
-		memprintf(err, "a positive 'len' argument is mandatory");
-		free(expr);
-		return -1;
+		if (!len) {
+			memprintf(err, "a positive 'len' argument is mandatory");
+			free(expr);
+			return -1;
+		}
+
+		hdr = calloc(sizeof(struct cap_hdr), 1);
+		hdr->next = px->req_cap;
+		hdr->name = NULL; /* not a header capture */
+		hdr->namelen = 0;
+		hdr->len = len;
+		hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
+		hdr->index = px->nb_req_cap++;
+
+		px->req_cap = hdr;
+		px->to_log |= LW_REQHDR;
+
+		rule->action       = HTTP_REQ_ACT_CUSTOM_CONT;
+		rule->action_ptr   = http_action_req_capture;
+		rule->arg.act.p[0] = expr;
+		rule->arg.act.p[1] = hdr;
 	}
 
+	else if (strcmp(args[cur_arg], "id") == 0) {
+		int id;
+		char *error;
 
-	hdr = calloc(sizeof(struct cap_hdr), 1);
-	hdr->next = px->req_cap;
-	hdr->name = NULL; /* not a header capture */
-	hdr->namelen = 0;
-	hdr->len = len;
-	hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
-	hdr->index = px->nb_req_cap++;
+		cur_arg++;
 
-	px->req_cap = hdr;
-	px->to_log |= LW_REQHDR;
+		if (!args[cur_arg]) {
+			memprintf(err, "missing id value");
+			free(expr);
+			return -1;
+		}
 
-	rule->action       = HTTP_REQ_ACT_CUSTOM_CONT;
-	rule->action_ptr   = http_action_req_capture;
-	rule->arg.act.p[0] = expr;
-	rule->arg.act.p[1] = hdr;
+		id = strtol(args[cur_arg], &error, 10);
+		if (*error != '\0') {
+			memprintf(err, "cannot parse id '%s'", args[cur_arg]);
+			free(expr);
+			return -1;
+		}
+		cur_arg++;
+
+		proxy->conf.args.ctx = ARGC_CAP;
+
+		rule->action       = HTTP_REQ_ACT_CUSTOM_CONT;
+		rule->action_ptr   = http_action_req_capture_by_id;
+		rule->arg.act.p[0] = expr;
+		rule->arg.act.p[1] = (void *)(long)id;
+	}
+
+	else {
+		memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
+		free(expr);
+		return -1;
+	}
 
 	*orig_arg = cur_arg;
 	return 0;