]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: capture: adds http-response capture
authorThierry FOURNIER <tfournier@haproxy.com>
Tue, 26 May 2015 16:06:31 +0000 (18:06 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 28 May 2015 11:51:00 +0000 (13:51 +0200)
This patch adds a http response capture keyword with the same behavior
as the previous patch called "MEDIUM: capture: Allow capture with slot
identifier".

doc/configuration.txt
src/proto_http.c

index fe03908b7d1f5f85a817509135971cf8034e0c4d..7984cadfa4a60a61c817df756861402408607a30 100644 (file)
@@ -3612,6 +3612,7 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
              about ACL usage.
 
 http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
+                capture <sample> id <id> |
                 set-header <name> <fmt> | del-header <name> |
                 replace-header <name> <regex-match> <replace-fmt> |
                 replace-value <name> <regex-match> <replace-fmt> |
@@ -3772,6 +3773,21 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
       parameter is the name of the function to run. The prototype of the
       function is documented in the API documentation.
 
+    - capture <sample> id <id> :
+      captures sample expression <sample> from the response buffer, and converts
+      it to a string. The resulting string is stored into the next request
+      "capture" slot, so it will possibly appear next to some captured HTTP
+      headers. It will then automatically appear in the logs, and it will be
+      possible to extract it using sample fetch rules to feed it into headers or
+      anything. Please check section 7.3 (Fetching samples) and "capture
+      response header" for more information.
+
+      The keyword "id" is the id of the capture slot which is used for storing
+      the string. The capture slot must be defined in an associated frontend.
+      This is useful to run captures in backends. The slot id can be declared by
+      a previous directive "http-response capture" or with the "declare capture"
+      keyword.
+
   There is no limit to the number of http-response statements per instance.
 
   It is important to know that http-response rules are processed very early in
index 0f46fb02ac9e2b2e9daf106c9df62b45363a5e7f..b311d74a25e13847ea64afd9836bc3fb9b2cf5a1 100644 (file)
@@ -12633,6 +12633,124 @@ int parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px, s
        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_req_action_kw_list http_req_actions = {
        }
 };
 
+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 @@ static void __http_protocol_init(void)
        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);
 }