diff --git a/doc/configuration.txt b/doc/configuration.txt index e0a59c6da..fe03908b7 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3278,7 +3278,7 @@ http-check send-state http-request { allow | deny | tarpit | auth [realm ] | redirect | add-header | set-header | - capture len | + capture [ len | id ] | del-header | set-nice | set-log-level | replace-header | replace-value | @@ -3497,7 +3497,7 @@ http-request { allow | deny | tarpit | auth [realm ] | redirect | 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 len : + - capture [ len | id ] : captures sample expression from the request buffer, and converts it to a string of at most 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 ] | redirect | 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 } [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 7a215c6fc..0f46fb02a 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -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 'len' "); + memprintf(err, "expects [ 'len' | id ]"); 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; }