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;