]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: http: add a new "http-request replace-uri" action
authorWilly Tarreau <w@1wt.eu>
Wed, 12 Jun 2019 15:44:02 +0000 (17:44 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 12 Jun 2019 16:06:59 +0000 (18:06 +0200)
This action is particularly convenient to replace some deprecated usees
of "reqrep". It takes a match and a format string including back-
references. The reqrep warning was updated to suggest it as well.

doc/configuration.txt
src/cfgparse-listen.c
src/http_act.c

index fa12a16f4cba87a7ea91b9d9dd1781adfd79e98a..bf1e4cb730c45b579d604f0abf43163a7c8c6fcd 100644 (file)
@@ -4447,6 +4447,33 @@ http-request replace-header <name> <match-regex> <replace-fmt>
 
       # assuming the backend IP is 192.168.1.20
 
+http-request replace-uri <match-regex> <replace-fmt>
+                           [ { if | unless } <condition> ]
+
+    This matches the regular expression in the URI part of the request
+    according to <match-regex>, and replaces it with the <replace-fmt>
+    argument. Standard back-references using the backslash ('\') followed by a
+    number are supported. The <fmt> field is interpreted as a log-format string
+    so it may contain special expressions just like the <fmt> argument passed
+    to "http-request set-uri". The match is exclusively case-sensitive. Any
+    optional scheme, authority or query string are considered in the matching
+    part of the URI. It is worth noting that regular expressions may be more
+    expensive to evaluate than certain ACLs, so rare replacements may benefit
+    from a condition to avoid performing the evaluation at all if it does not
+    match.
+
+    Example:
+        # prefix /foo : turn /bar?q=1 into /foo/bar?q=1 :
+        http-request replace-uri (.*) /foo\1
+
+        # suffix /foo : turn /bar?q=1 into /bar/foo?q=1 :
+        http-request replace-uri ([^?]*)(\?(.*))? \1/foo\2
+
+        # strip /foo : turn /foo/bar?q=1 into /bar?q=1
+        http-request replace-uri /foo/(.*) /\1
+        # or more efficient if only some requests match :
+        http-request replace-uri /foo/(.*) /\1 if { url_beg /foo/ }
+
 http-request replace-value <name> <match-regex> <replace-fmt>
                            [ { if | unless } <condition> ]
 
index d659779a842c5578d18784da0856030edeee5792..6c18bfc1a474d16188a4226835f9501f8e00548a 100644 (file)
@@ -3866,7 +3866,7 @@ stats_error_parsing:
        }
        else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) {  /* replace request header from a regex */
                if (!already_warned(WARN_REQREP_DEPRECATED))
-                       ha_warning("parsing [%s:%d] : The '%s' directive is deprecated in favor of 'http-request replace-header' and will be removed in next version.\n", file, linenum, args[0]);
+                       ha_warning("parsing [%s:%d] : The '%s' directive is deprecated in favor of 'http-request replace-uri' and 'http-request replace-header' and will be removed in next version.\n", file, linenum, args[0]);
 
                if (*(args[2]) == 0) {
                        ha_alert("parsing [%s:%d] : '%s' expects <search> and <replace> as arguments.\n",
index daa789abb6dfa5823a662155547ad1c616d23068..65d9595c0196da8cae0c55c2a1594ca5f5f26274 100644 (file)
@@ -32,6 +32,7 @@
 #include <proto/acl.h>
 #include <proto/arg.h>
 #include <proto/http_rules.h>
+#include <proto/http_htx.h>
 #include <proto/log.h>
 #include <proto/proto_http.h>
 #include <proto/stream_interface.h>
@@ -128,6 +129,93 @@ static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, s
        return ACT_RET_PRS_OK;
 }
 
+/* This function executes a replace-uri action. It finds its arguments in
+ * <rule>.arg.act.p[]. It builds a string in the trash from the format string
+ * previously filled by function parse_replace_uri() and will execute the regex
+ * in p[1] to replace the URI. It uses the format string present in act.p[2..3].
+ * It always returns ACT_RET_CONT. If an error occurs, the action is canceled,
+ * but the rule processing continues.
+ */
+static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
+                                               struct session *sess, struct stream *s, int flags)
+{
+       enum act_return ret = ACT_RET_ERR;
+       struct buffer *replace, *output;
+       struct ist uri;
+       int len;
+
+       replace = alloc_trash_chunk();
+       output  = alloc_trash_chunk();
+       if (!replace || !output)
+               goto leave;
+
+       if (IS_HTX_STRM(s))
+               uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
+       else
+               uri = ist2(ci_head(&s->req) + s->txn->req.sl.rq.u, s->txn->req.sl.rq.u_l);
+
+       if (!regex_exec_match2(rule->arg.act.p[1], uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
+               goto leave;
+
+       replace->data = build_logline(s, replace->area, replace->size, (struct list *)&rule->arg.act.p[2]);
+
+       /* note: uri.ptr doesn't need to be zero-terminated because it will
+        * only be used to pick pmatch references.
+        */
+       len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
+       if (len == -1)
+               goto leave;
+
+       /* 3 is the set-uri action */
+       http_replace_req_line(3, output->area, len, px, s);
+
+       ret = ACT_RET_CONT;
+
+leave:
+       free_trash_chunk(output);
+       free_trash_chunk(replace);
+       return ret;
+}
+
+/* parse a "replace-uri" http-request action.
+ * This action takes 2 arguments (a regex and a replacement format string).
+ * The resulting rule makes use of arg->act.p[0] to store the action (0 for now),
+ * p[1] to store the compiled regex, and arg->act.p[2..3] to store the log-format
+ * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
+                                            struct act_rule *rule, char **err)
+{
+       int cur_arg = *orig_arg;
+       char *error = NULL;
+
+       rule->action = ACT_CUSTOM;
+       rule->arg.act.p[0] = (void *)0; // replace-uri
+       rule->action_ptr = http_action_replace_uri;
+
+       if (!*args[cur_arg] || !*args[cur_arg+1] ||
+           (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
+               memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
+               return ACT_RET_PRS_ERR;
+       }
+
+       if (!(rule->arg.act.p[1] = regex_comp(args[cur_arg], 1, 1, &error))) {
+               memprintf(err, "failed to parse the regex : %s", error);
+               free(error);
+               return ACT_RET_PRS_ERR;
+       }
+
+       LIST_INIT((struct list *)&rule->arg.act.p[2]);
+       px->conf.args.ctx = ARGC_HRQ;
+       if (!parse_logformat_string(args[cur_arg + 1], px, (struct list *)&rule->arg.act.p[2], LOG_OPT_HTTP,
+                                   (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
+               return ACT_RET_PRS_ERR;
+       }
+
+       (*orig_arg) += 2;
+       return ACT_RET_PRS_OK;
+}
+
 /* This function is just a compliant action wrapper for "set-status". */
 static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
                                               struct session *sess, struct stream *s, int flags)
@@ -608,6 +696,7 @@ static struct action_kw_list http_req_actions = {
                { "capture",    parse_http_req_capture },
                { "reject",     parse_http_action_reject },
                { "disable-l7-retry", parse_http_req_disable_l7_retry },
+               { "replace-uri", parse_replace_uri },
                { "set-method", parse_set_req_line },
                { "set-path",   parse_set_req_line },
                { "set-query",  parse_set_req_line },