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.
This commit is contained in:
Willy Tarreau 2019-06-12 17:44:02 +02:00
parent c9515529a0
commit 3381022d88
3 changed files with 117 additions and 1 deletions

View 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> ]

View 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",

View 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 },