]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: capture: Allow capture with slot identifier
authorThierry FOURNIER <tfournier@haproxy.com>
Tue, 26 May 2015 15:58:29 +0000 (17:58 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 28 May 2015 11:50:29 +0000 (13:50 +0200)
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]);

doc/configuration.txt
src/proto_http.c

index e0a59c6da8bd26a37c0eeae0fb60fdafcb82f015..fe03908b7d1f5f85a817509135971cf8034e0c4d 100644 (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
index 7a215c6fc71288d71121fe5c552b360b31a55964..0f46fb02ac9e2b2e9daf106c9df62b45363a5e7f 100644 (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,30 +12571,63 @@ 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;
+               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;
+
+               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++;
 
-       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++;
+               proxy->conf.args.ctx = ARGC_CAP;
 
-       px->req_cap = hdr;
-       px->to_log |= LW_REQHDR;
+               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;
+       }
 
-       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 {
+               memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
+               free(expr);
+               return -1;
+       }
 
        *orig_arg = cur_arg;
        return 0;