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
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 9dbe432..69da6be 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4687,9 +4687,13 @@
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 @@
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 @@
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> ]
diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h
index a002153..36aa5bb 100644
--- a/include/haproxy/action-t.h
+++ b/include/haproxy/action-t.h
@@ -78,7 +78,6 @@
ACT_ACTION_DENY,
/* common http actions .*/
- ACT_HTTP_DEL_HDR,
ACT_HTTP_REDIR,
ACT_HTTP_SET_NICE,
ACT_HTTP_SET_LOGL,
diff --git a/reg-tests/http-rules/del_header.vtc b/reg-tests/http-rules/del_header.vtc
new file mode 100644
index 0000000..32a7a70
--- /dev/null
+++ b/reg-tests/http-rules/del_header.vtc
@@ -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
diff --git a/src/http_act.c b/src/http_act.c
index 27db478..13a5c37 100644
--- a/src/http_act.c
+++ b/src/http_act.c
@@ -1438,20 +1438,67 @@
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 @@
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);
+ 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;
+ }
+ }
+
- LIST_INIT(&rule->arg.http.fmt);
*orig_arg = cur_arg + 1;
return ACT_RET_PRS_OK;
}
diff --git a/src/http_ana.c b/src/http_ana.c
index 7a9cd0b..d89b7d5 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -2838,14 +2838,10 @@
{
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 @@
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 @@
{
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 @@
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))