MINOR: http: add a new "http-request replace-uri" action
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.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index fa12a16..bf1e4cb 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4447,6 +4447,33 @@
# 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> ]
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index d659779..6c18bfc 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -3866,7 +3866,7 @@
}
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",
diff --git a/src/http_act.c b/src/http_act.c
index daa789a..65d9595 100644
--- a/src/http_act.c
+++ b/src/http_act.c
@@ -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 @@
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 @@
{ "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 },