MINOR: http_act: Add -m flag for del-header name matching method

This patch adds -m flag which allows to specify header name
matching method when deleting headers from http request/response.
Currently beg, end, sub, str and reg are supported.

This is related to GitHub issue #909
This commit is contained in:
Maciej Zdeb 2020-11-20 13:58:48 +00:00 committed by Willy Tarreau
parent 302b9f8d7a
commit ebdd4c55da
5 changed files with 188 additions and 34 deletions

View File

@ -4687,9 +4687,13 @@ http-after-response allow [ { if | unless } <condition> ]
This stops the evaluation of the rules and lets the response pass the check.
No further "http-after-response" rules are evaluated.
http-after-response del-header <name> [ { if | unless } <condition> ]
http-after-response del-header <name> [ -m <meth> ] [ { if | unless } <condition> ]
This removes all HTTP header fields whose name is specified in <name>.
This removes all HTTP header fields whose name is specified in <name>. <meth>
is the matching method, applied on the header name. Supported matching methods
are "str" (exact match), "beg" (prefix match), "end" (suffix match), "sub"
(substring match) and "reg" (regex match). If not specified, exact matching
method is used.
http-after-response replace-header <name> <regex-match> <replace-fmt>
[ { if | unless } <condition> ]
@ -5461,9 +5465,13 @@ http-request del-acl(<file-name>) <key fmt> [ { if | unless } <condition> ]
It is the equivalent of the "del acl" command from the stats socket, but can
be triggered by an HTTP request.
http-request del-header <name> [ { if | unless } <condition> ]
http-request del-header <name> [ -m <meth> ] [ { if | unless } <condition> ]
This removes all HTTP header fields whose name is specified in <name>.
This removes all HTTP header fields whose name is specified in <name>. <meth>
is the matching method, applied on the header name. Supported matching methods
are "str" (exact match), "beg" (prefix match), "end" (suffix match), "sub"
(substring match) and "reg" (regex match). If not specified, exact matching
method is used.
http-request del-map(<file-name>) <key fmt> [ { if | unless } <condition> ]
@ -6274,9 +6282,13 @@ http-response del-acl(<file-name>) <key fmt> [ { if | unless } <condition> ]
It is the equivalent of the "del acl" command from the stats socket, but can
be triggered by an HTTP response.
http-response del-header <name> [ { if | unless } <condition> ]
http-response del-header <name> [ -m <meth> ] [ { if | unless } <condition> ]
This removes all HTTP header fields whose name is specified in <name>.
This removes all HTTP header fields whose name is specified in <name>. <meth>
is the matching method, applied on the header name. Supported matching methods
are "str" (exact match), "beg" (prefix match), "end" (suffix match), "sub"
(substring match) and "reg" (regex match). If not specified, exact matching
method is used.
http-response del-map(<file-name>) <key fmt> [ { if | unless } <condition> ]

View File

@ -78,7 +78,6 @@ enum act_name {
ACT_ACTION_DENY,
/* common http actions .*/
ACT_HTTP_DEL_HDR,
ACT_HTTP_REDIR,
ACT_HTTP_SET_NICE,
ACT_HTTP_SET_LOGL,

View File

@ -0,0 +1,93 @@
varnishtest "del-header tests"
# This config tests various http-request/response del-header operations
# with or without specified header name matching method.
feature ignore_unknown_macro
server s1 {
rxreq
expect req.url == /
expect req.http.x-always == always
expect req.http.x-str1 == <undef>
expect req.http.x-str2 == <undef>
expect req.http.x-beg1 == <undef>
expect req.http.x-beg2 == <undef>
expect req.http.x-end1 == <undef>
expect req.http.x-end2 == end2
expect req.http.x-sub1 == <undef>
expect req.http.x-sub2 == <undef>
expect req.http.x-reg1 == <undef>
expect req.http.x-reg2 == <undef>
txresp -hdr "x-always: always" \
-hdr "x-str1: str1" \
-hdr "x-str2: str2" \
-hdr "x-beg1: beg1" \
-hdr "x-beg2: beg2" \
-hdr "x-end1: end1" \
-hdr "x-end2: end2" \
-hdr "x-sub1: sub1" \
-hdr "x-sub2: sub2" \
-hdr "x-reg1: reg1" \
-hdr "x-reg2: reg2"
} -start
haproxy h1 -conf {
defaults
mode http
timeout connect 1s
timeout client 1s
timeout server 1s
frontend fe
bind "fd@${fe}"
http-request del-header x-str1
http-request del-header x-str2 -m str
http-request del-header x-beg -m beg
http-request del-header end1 -m end
http-request del-header sub -m sub
http-request del-header ^x.reg.$ -m reg
http-response del-header x-str1
http-response del-header x-str2 -m str
http-response del-header x-beg -m beg
http-response del-header end1 -m end
http-response del-header sub -m sub
http-response del-header ^x.reg.$ -m reg
default_backend be
backend be
server s1 ${s1_addr}:${s1_port}
} -start
client c1 -connect ${h1_fe_sock} {
txreq -req GET -url / \
-hdr "x-always: always" \
-hdr "x-str1: str1" \
-hdr "x-str2: str2" \
-hdr "x-beg1: beg1" \
-hdr "x-beg2: beg2" \
-hdr "x-end1: end1" \
-hdr "x-end2: end2" \
-hdr "x-sub1: sub1" \
-hdr "x-sub2: sub2" \
-hdr "x-reg1: reg1" \
-hdr "x-reg2: reg2"
rxresp
expect resp.status == 200
expect resp.http.x-always == always
expect resp.http.x-str1 == <undef>
expect resp.http.x-str2 == <undef>
expect resp.http.x-beg1 == <undef>
expect resp.http.x-beg2 == <undef>
expect resp.http.x-end1 == <undef>
expect resp.http.x-end2 == end2
expect resp.http.x-sub1 == <undef>
expect resp.http.x-sub2 == <undef>
expect resp.http.x-reg1 == <undef>
expect resp.http.x-reg2 == <undef>
} -run

View File

@ -1438,20 +1438,67 @@ static enum act_parse_ret parse_http_replace_header(const char **args, int *orig
return ACT_RET_PRS_OK;
}
/* Parse a "del-header" action. It takes an header name as argument. It returns
* ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
/* This function executes a del-header action with selected matching mode for
* header name. It finds the matching method to be performed in <.action>, previously
* filled by function parse_http_del_header(). On success, it returns ACT_RET_CONT.
* Otherwise ACT_RET_ERR is returned.
*/
static enum act_return http_action_del_header(struct act_rule *rule, struct proxy *px,
struct session *sess, struct stream *s, int flags)
{
struct http_hdr_ctx ctx;
struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn->req : &s->txn->rsp);
struct htx *htx = htxbuf(&msg->chn->buf);
enum act_return ret = ACT_RET_CONT;
/* remove all occurrences of the header */
ctx.blk = NULL;
switch (rule->action) {
case PAT_MATCH_STR:
while (http_find_header(htx, rule->arg.http.str, &ctx, 1))
http_remove_header(htx, &ctx);
break;
case PAT_MATCH_BEG:
while (http_find_pfx_header(htx, rule->arg.http.str, &ctx, 1))
http_remove_header(htx, &ctx);
break;
case PAT_MATCH_END:
while (http_find_sfx_header(htx, rule->arg.http.str, &ctx, 1))
http_remove_header(htx, &ctx);
break;
case PAT_MATCH_SUB:
while (http_find_sub_header(htx, rule->arg.http.str, &ctx, 1))
http_remove_header(htx, &ctx);
break;
case PAT_MATCH_REG:
while (http_match_header(htx, rule->arg.http.re, &ctx, 1))
http_remove_header(htx, &ctx);
break;
default:
return ACT_RET_ERR;
}
return ret;
}
/* Parse a "del-header" action. It takes string as a required argument,
* optional flag (currently only -m) and optional matching method of input string
* with header name to be deleted. Default matching method is exact match (-m str).
* It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
*/
static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
struct act_rule *rule, char **err)
{
int cur_arg;
int pat_idx;
rule->action = ACT_HTTP_DEL_HDR;
/* set exact matching (-m str) as default */
rule->action = PAT_MATCH_STR;
rule->action_ptr = http_action_del_header;
rule->release_ptr = release_http_action;
cur_arg = *orig_arg;
if (!*args[cur_arg]) {
memprintf(err, "expects exactly 1 arguments");
memprintf(err, "expects at least 1 argument");
return ACT_RET_PRS_ERR;
}
@ -1459,7 +1506,32 @@ static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg
rule->arg.http.str.len = strlen(rule->arg.http.str.ptr);
px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
LIST_INIT(&rule->arg.http.fmt);
if (strcmp(args[cur_arg+1], "-m") == 0) {
cur_arg++;
if (!*args[cur_arg+1]) {
memprintf(err, "-m flag expects exactly 1 argument");
return ACT_RET_PRS_ERR;
}
cur_arg++;
pat_idx = pat_find_match_name(args[cur_arg]);
switch (pat_idx) {
case PAT_MATCH_REG:
if (!(rule->arg.http.re = regex_comp(rule->arg.http.str.ptr, 1, 1, err)))
return ACT_RET_PRS_ERR;
/* fall through */
case PAT_MATCH_STR:
case PAT_MATCH_BEG:
case PAT_MATCH_END:
case PAT_MATCH_SUB:
rule->action = pat_idx;
break;
default:
memprintf(err, "-m with unsupported matching method '%s'", args[cur_arg]);
return ACT_RET_PRS_ERR;
}
}
*orig_arg = cur_arg + 1;
return ACT_RET_PRS_OK;
}

View File

@ -2838,14 +2838,10 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis
{
struct session *sess = strm_sess(s);
struct http_txn *txn = s->txn;
struct htx *htx;
struct act_rule *rule;
struct http_hdr_ctx ctx;
enum rule_result rule_ret = HTTP_RULE_RES_CONT;
int act_opts = 0;
htx = htxbuf(&s->req.buf);
/* If "the current_rule_list" match the executed rule list, we are in
* resume condition. If a resume is needed it is always in the action
* and never in the ACL or converters. In this case, we initialise the
@ -2958,13 +2954,6 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis
s->logs.level = rule->arg.http.i;
break;
case ACT_HTTP_DEL_HDR:
/* remove all occurrences of the header */
ctx.blk = NULL;
while (http_find_header(htx, rule->arg.http.str, &ctx, 1))
http_remove_header(htx, &ctx);
break;
/* other flags exists, but normally, they never be matched. */
default:
break;
@ -2994,14 +2983,10 @@ static enum rule_result http_res_get_intercept_rule(struct proxy *px, struct lis
{
struct session *sess = strm_sess(s);
struct http_txn *txn = s->txn;
struct htx *htx;
struct act_rule *rule;
struct http_hdr_ctx ctx;
enum rule_result rule_ret = HTTP_RULE_RES_CONT;
int act_opts = 0;
htx = htxbuf(&s->res.buf);
/* If "the current_rule_list" match the executed rule list, we are in
* resume condition. If a resume is needed it is always in the action
* and never in the ACL or converters. In this case, we initialise the
@ -3102,13 +3087,6 @@ resume_execution:
s->logs.level = rule->arg.http.i;
break;
case ACT_HTTP_DEL_HDR:
/* remove all occurrences of the header */
ctx.blk = NULL;
while (http_find_header(htx, rule->arg.http.str, &ctx, 1))
http_remove_header(htx, &ctx);
break;
case ACT_HTTP_REDIR:
rule_ret = HTTP_RULE_RES_ABRT;
if (!http_apply_redirect_rule(rule->arg.redir, s, txn))