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]);
This commit is contained in:
Thierry FOURNIER 2015-05-26 17:58:29 +02:00 committed by Willy Tarreau
parent 35ab27561e
commit 82bf70dff4
2 changed files with 118 additions and 31 deletions

View File

@ -3278,7 +3278,7 @@ http-check send-state
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 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
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 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
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

View File

@ -12464,9 +12464,52 @@ int http_action_req_capture(struct http_req_rule *rule, struct proxy *px, struct
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 @@ int parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px, s
break;
if (cur_arg < *orig_arg + 3) {
memprintf(err, "expects <expression> 'len' <length> ");
memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
return -1;
}
if (!(px->cap & PR_CAP_FE)) {
memprintf(err, "proxy '%s' has no frontend capability", px->id);
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 @@ int parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px, s
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,31 +12571,64 @@ int parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px, s
return -1;
}
cur_arg++;
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;
}
if (!len) {
memprintf(err, "a positive 'len' argument is mandatory");
else if (strcmp(args[cur_arg], "id") == 0) {
int id;
char *error;
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++;
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;
}
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;
*orig_arg = cur_arg;
return 0;
}