diff --git a/doc/configuration.txt b/doc/configuration.txt index 9dbe43208..69da6bee2 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4687,9 +4687,13 @@ http-after-response allow [ { if | unless } ] 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 [ { if | unless } ] +http-after-response del-header [ -m ] [ { if | unless } ] - This removes all HTTP header fields whose name is specified in . + This removes all HTTP header fields whose name is specified in . + 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 [ { if | unless } ] @@ -5461,9 +5465,13 @@ http-request del-acl() [ { if | unless } ] 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 [ { if | unless } ] +http-request del-header [ -m ] [ { if | unless } ] - This removes all HTTP header fields whose name is specified in . + This removes all HTTP header fields whose name is specified in . + 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() [ { if | unless } ] @@ -6274,9 +6282,13 @@ http-response del-acl() [ { if | unless } ] 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 [ { if | unless } ] +http-response del-header [ -m ] [ { if | unless } ] - This removes all HTTP header fields whose name is specified in . + This removes all HTTP header fields whose name is specified in . + 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() [ { if | unless } ] diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h index a002153d7..36aa5bbdc 100644 --- a/include/haproxy/action-t.h +++ b/include/haproxy/action-t.h @@ -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, diff --git a/reg-tests/http-rules/del_header.vtc b/reg-tests/http-rules/del_header.vtc new file mode 100644 index 000000000..32a7a70a1 --- /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 == + expect req.http.x-str2 == + expect req.http.x-beg1 == + expect req.http.x-beg2 == + expect req.http.x-end1 == + expect req.http.x-end2 == end2 + expect req.http.x-sub1 == + expect req.http.x-sub2 == + expect req.http.x-reg1 == + expect req.http.x-reg2 == + 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 == + expect resp.http.x-str2 == + expect resp.http.x-beg1 == + expect resp.http.x-beg2 == + expect resp.http.x-end1 == + expect resp.http.x-end2 == end2 + expect resp.http.x-sub1 == + expect resp.http.x-sub2 == + expect resp.http.x-reg1 == + expect resp.http.x-reg2 == +} -run diff --git a/src/http_act.c b/src/http_act.c index 27db47833..13a5c370d 100644 --- a/src/http_act.c +++ b/src/http_act.c @@ -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; } diff --git a/src/http_ana.c b/src/http_ana.c index 7a9cd0bbc..d89b7d5d7 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -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))