MEDIUM: capture: adds http-response capture

This patch adds a http response capture keyword with the same behavior
as the previous patch called "MEDIUM: capture: Allow capture with slot
identifier".
diff --git a/src/proto_http.c b/src/proto_http.c
index 0f46fb0..b311d74 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -12633,6 +12633,124 @@
 	return 0;
 }
 
+/* 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_res_capture_by_id(struct http_res_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->res_cap;
+	struct proxy *fe = strm_fe(s);
+	int len;
+	int i;
+
+	/* Look for the original configuration. */
+	for (h = fe->rsp_cap, i = fe->nb_rsp_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_RES|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-response 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 od the preallocated id into arg->act.p[1].
+ * It returns 0 on success, < 0 on error.
+ */
+int parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px, struct http_res_rule *rule, char **err)
+{
+	struct sample_expr *expr;
+	int cur_arg;
+	int id;
+	char *error;
+
+	for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
+		if (strcmp(args[cur_arg], "if") == 0 ||
+		    strcmp(args[cur_arg], "unless") == 0)
+			break;
+
+	if (cur_arg < *orig_arg + 3) {
+		memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
+		return -1;
+	}
+
+	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)
+		return -1;
+
+	if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
+		memprintf(err,
+			  "fetch method '%s' extracts information from '%s', none of which is available here",
+			  args[cur_arg-1], sample_src_names(expr->fetch->use));
+		free(expr);
+		return -1;
+	}
+
+	if (!args[cur_arg] || !*args[cur_arg]) {
+		memprintf(err, "expects 'len or 'id'");
+		free(expr);
+		return -1;
+	}
+
+	if (strcmp(args[cur_arg], "id") != 0) {
+		memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
+		free(expr);
+		return -1;
+	}
+
+	cur_arg++;
+
+	if (!args[cur_arg]) {
+		memprintf(err, "missing id value");
+		free(expr);
+		return -1;
+	}
+
+	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++;
+
+	LIST_INIT((struct list *)&rule->arg.act.p[0]);
+	proxy->conf.args.ctx = ARGC_CAP;
+
+	rule->action       = HTTP_RES_ACT_CUSTOM_CONT;
+	rule->action_ptr   = http_action_res_capture_by_id;
+	rule->arg.act.p[0] = expr;
+	rule->arg.act.p[1] = (void *)(long)id;
+
+	*orig_arg = cur_arg;
+	return 0;
+}
+
 /*
  * Return the struct http_req_action_kw associated to a keyword.
  */
@@ -12908,6 +13026,14 @@
 	}
 };
 
+struct http_res_action_kw_list http_res_actions = {
+	.scope = "http",
+	.kw = {
+		{ "capture",    parse_http_res_capture },
+		{ NULL, NULL }
+	}
+};
+
 __attribute__((constructor))
 static void __http_protocol_init(void)
 {
@@ -12915,6 +13041,7 @@
 	sample_register_fetches(&sample_fetch_keywords);
 	sample_register_convs(&sample_conv_kws);
 	http_req_keywords_register(&http_req_actions);
+	http_res_keywords_register(&http_res_actions);
 }