From: Willy Tarreau Date: Wed, 12 Jun 2019 15:44:02 +0000 (+0200) Subject: MINOR: http: add a new "http-request replace-uri" action X-Git-Tag: v2.0.0~61 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3381022d88764549aab91dcc1aeedf5e95580f76;p=thirdparty%2Fhaproxy.git 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 fa12a16f4c..bf1e4cb730 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4447,6 +4447,33 @@ http-request replace-header # assuming the backend IP is 192.168.1.20 +http-request replace-uri + [ { if | unless } ] + + This matches the regular expression in the URI part of the request + according to , and replaces it with the + argument. Standard back-references using the backslash ('\') followed by a + number are supported. The field is interpreted as a log-format string + so it may contain special expressions just like the 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 [ { if | unless } ] diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index d659779a84..6c18bfc1a4 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -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 and as arguments.\n", diff --git a/src/http_act.c b/src/http_act.c index daa789abb6..65d9595c01 100644 --- a/src/http_act.c +++ b/src/http_act.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -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 + * .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 and "); + 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 },