Hi Sasha,
On Sat, Jun 07, 2014 at 07:36:48AM -0600, Sasha Pachev wrote:
> On Thu, Jun 5, 2014 at 11:40 AM, Sasha Pachev <[email protected]> wrote:
> > Thierry:
> >
> > Will try to get this done in the next day. I assume you want the docs
> > as a patch to the documentation file, right?
> >
> > On Thu, Jun 5, 2014 at 7:15 AM, Thierry FOURNIER
> > <[email protected]> wrote:
> >> Willy have the last word :) The doc is missing. Can you do this ?
>
> Attached is the patch including the documentation.
Thanks. I was about to apply it with a number of fixes, but one (see below)
blocked me so we'll have to discuss it first.
First, there were *a lot* of indent and space/tabs issues that I had to fix
by hand, I think your editor needs some configuration or maybe even it hides
all the indentation from you. I suggest you set "color.ui=true" in your git
config since you used git, it will immediately show you most of them.
Just a small example :
static int http_regfix_header(struct session* s, struct http_msg *msg, const
char* name, uint name_len,
char* buf, struct hdr_idx* idx, struct list *fmt, regex_t* re,
struct hdr_ctx* ctx, int action)
{
ctx->idx = 0;
while (http_find_full_header2(name, name_len, buf, idx, ctx))
{
struct hdr_idx_elem *hdr = idx->v + ctx->idx;
int delta;
char* val = (char*)ctx->line + name_len + 2;
char* val_end = (char*)ctx->line + hdr->len;
char save_val_end = *val_end;
char* reg_dst_buf;
uint reg_dst_buf_size;
int n_replaced;
*val_end = 0;
trash.len = build_logline(s, trash.str, trash.size, fmt);
(...)
Second, I removed the macro ACTION_IS_REGFIX(rule,req_or_res). It was only
used at one place now and mostly resulted in obfuscation than any savings,
and you already have a flag you use to get the same information 5 lines
below ("regfix_rule").
Third, I'm having some doubts about function http_regfix_header() :
it *seems* like http_regfix_replace() and http_regfix_modify() return the
number of chars of the output string, though the later may return -1 in
case the string doesn't fit into the output buffer (you need to document
that at the top of the functions, it's really not clear). Then function
http_regfix_header() doesn't perform the replacement if the output string
is finally empty, which means that it cannot be used to remove the undesired
part of a string. I'm really not convinced this is the intended behaviour.
Also, this function seems to return either 0 or -1, it would be nice to
document the output values as well (eg: 0 on success otherwise error or
conversely).
Fourth, the error code above was turned into HTTP_RULE_RES_ABRT which
as I'm seeing is meant for auth, stats, etc... So I fixed it using
HTTP_RULE_RES_BADREQ which is used for these cases.
Fifth, the name "regfix" doesn't tell me anything so it's really not clear
to me when reading the code that it's about modify-header and replace-header,
thus I renamed the following symbols :
http_regfix_replace() -> http_replace_header()
http_regfix_modify() -> http_modify_header()
http_regfix_header() -> http_transform_header()
regfix_rule -> is_hdr_rule
I know that it's purely cosmetics but it helps maintaining the code. If you
have better names to suggest, feel free to do so, of course!
My real problem comes from this :
n_replaced = http_modify_header(re, reg_dst_buf, reg_dst_buf_size, val,
';', trash.str);
You're using the semi-colon as a header delimiter, which is wrong, the
delimiter is the comma. The semi-colon is an attribute delimiter only
(http_find_header2() would have done it transparently for you but that
would make the replacement less efficient so that makes sense to keep
http_find_full_header2() and do the check by hand). And I'm seeing in
the doc that your examples count on this, so I'd like to be sure about
your use case (if you actually use it or you just had to build these
examples for testing purposes and for the doc). Using the semi-colon
here will span over multiple headers and cause a lot of breakage.
However, your syntax easily allows one to match everything but a
semi-colon when needed, so that should not be an issue at all. If we
restart from your documentation example :
http-response modify-header Set-Cookie (bar=bar.*) \1;ip=%bi
This converts:
Set-Cookie: bar=barbarian; foo=foo; bar=barcode
into:
Set-Cookie: bar=barbarian;ip=192.168.1.20; foo=foo;
bar=barcode;ip=192.168.1.20
It should be done this way instead :
http-response modify-header Set-Cookie (bar=bar[^;]*) \1;ip=%bi
Note that even the above still matches "foobar=barcode". Another point just
came to my mind, maybe some people will want the ability to change a full
header (typically one involving commas like the Date, Expires or Set-Cookie
headers). We already introduced some matches for these kind of headers in
the ACLs using "fhdr_*" instead of "hdr_*". Maybe it's something we could
consider here for the modify action since adding it comes with a very low
cost, what do you think ?
So I'm attaching the cleaned-up patch in its current form so that you can
double-check, but I stopped before performing the ';' -> ',' operation.
Also feel free to reword the tiny commit message I wrote if you want.
Thanks,
Willy
>From 32dfefc463503fccd31534e983d3122c98f9c0f7 Mon Sep 17 00:00:00 2001
From: Sasha Pachev <[email protected]>
Date: Sat, 7 Jun 2014 07:36:48 -0600
Subject: MINOR: http: add 2 new http actions : modify-header and
replace-header
This patch implements new "modify-header" and "replace-header" actions
for the http-request and http-response directives.
---
doc/configuration.txt | 60 ++++++++++
include/proto/proto_http.h | 1 +
include/types/proto_http.h | 6 +
src/haproxy.c | 1 +
src/proto_http.c | 274 ++++++++++++++++++++++++++++++++++++++++++---
5 files changed, 327 insertions(+), 15 deletions(-)
diff --git a/doc/configuration.txt b/doc/configuration.txt
index d28c91a..74d2c5e 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2867,6 +2867,8 @@ http-check send-state
http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
add-header <name> <fmt> | set-header <name> <fmt> |
del-header <name> | set-nice <nice> | set-log-level <level> |
+ replace-header <name> <match-regex> <replace-fmt> |
+ modify-header <name> <match-regex> <replace-fmt> |
set-tos <tos> | set-mark <mark> |
add-acl(<file name>) <key fmt> |
del-acl(<file name>) <key fmt> |
@@ -2934,6 +2936,34 @@ http-request { allow | deny | tarpit | auth [realm
<realm>] | redirect <rule> |
- "del-header" removes all HTTP header fields whose name is specified in
<name>.
+ - "replace-header" matches the regular expression in the header value
+ according to match-regex and replaces it with the replace-fmt argument.
+ Format characters are allowed in replace-fmt and work like in fmt
+ arguments in add-header. The match is case-sensitive.
+
+ Example:
+
+ http-request replace-header Set-Cookie (bar=bar.*) \1;id=%Ts:%pid
+
+ - "modify-header" works like "replace-header" except it matches and
replaces
+ every part of the value delimited by ; instead of the entire header.
+
+ Example:
+
+ http-response modify-header Set-Cookie (bar=bar.*) \1;ip=%bi
+
+ This converts:
+
+ Set-Cookie: bar=barbarian; foo=foo; bar=barcode
+
+ into:
+
+ Set-Cookie: bar=barbarian;ip=192.168.1.20; foo=foo; bar=barcode;ip=\
+ 192.168.1.20
+
+ assuming the backend IP is 192.168.1.20
+
+
- "set-nice" sets the "nice" factor of the current request being processed.
It only has effect against the other requests being processed at the same
time. The default value is 0, unless altered by the "nice" setting on the
@@ -3058,6 +3088,8 @@ http-request { allow | deny | tarpit | auth [realm
<realm>] | redirect <rule> |
http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
set-header <name> <fmt> | del-header <name> |
+ replace-header <name> <regex-match> <replace-fmt> |
+ modify-header <name> <regex-match> <replace-fmt> |
set-log-level <level> | set-mark <mark> | set-tos <tos> |
add-acl(<file name>) <key fmt> |
del-acl(<file name>) <key fmt> |
@@ -3102,6 +3134,34 @@ http-response { allow | deny | add-header <name> <fmt> |
set-nice <nice> |
- "del-header" removes all HTTP header fields whose name is specified in
<name>.
+ - "replace-header" matches the regular expression in the header value
+ according to match-regex and replaces it with the replace-fmt argument.
+ Format characters are allowed in replace-fmt and work like in fmt
+ arguments in add-header. The match is case-sensitive.
+
+ Example:
+
+ http-response replace-header Set-Cookie (bar=bar.*) \1;id=%Ts:%pid
+
+ - "modify-header" works like "replace-header" except it matches and
replaces
+ every part of the value delimited by ; instead of the entire header.
+
+ Example:
+
+ http-response modify-header Set-Cookie (bar=bar.*) \1;ip=%bi
+
+ This converts:
+
+ Set-Cookie: bar=barbarian; foo=foo; bar=barcode
+
+ into:
+
+ Set-Cookie: bar=barbarian;ip=192.168.1.20; foo=foo; bar=barcode;ip=\
+ 192.168.1.20
+
+ assuming the backend IP is 192.168.1.20
+
+
- "set-nice" sets the "nice" factor of the current request being processed.
It only has effect against the other requests being processed at the same
time. The default value is 0, unless altered by the "nice" setting on the
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index 6370e2d..e898ca8 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -116,6 +116,7 @@ void http_reset_txn(struct session *s);
struct http_req_rule *parse_http_req_cond(const char **args, const char *file,
int linenum, struct proxy *proxy);
struct http_res_rule *parse_http_res_cond(const char **args, const char *file,
int linenum, struct proxy *proxy);
void free_http_req_rules(struct list *r);
+void free_http_res_rules(struct list *r);
struct chunk *http_error_message(struct session *s, int msgnum);
struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum,
struct proxy *curproxy,
const char **args, char
**errmsg, int use_fmt);
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index f5dd9a3..d5478a4 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -247,6 +247,8 @@ enum {
HTTP_REQ_ACT_ADD_HDR,
HTTP_REQ_ACT_SET_HDR,
HTTP_REQ_ACT_DEL_HDR,
+ HTTP_REQ_ACT_REPLACE_HDR,
+ HTTP_REQ_ACT_MODIFY_HDR,
HTTP_REQ_ACT_REDIR,
HTTP_REQ_ACT_SET_NICE,
HTTP_REQ_ACT_SET_LOGL,
@@ -267,6 +269,8 @@ enum {
HTTP_RES_ACT_ALLOW,
HTTP_RES_ACT_DENY,
HTTP_RES_ACT_ADD_HDR,
+ HTTP_RES_ACT_MODIFY_HDR,
+ HTTP_RES_ACT_REPLACE_HDR,
HTTP_RES_ACT_SET_HDR,
HTTP_RES_ACT_DEL_HDR,
HTTP_RES_ACT_SET_NICE,
@@ -415,6 +419,7 @@ struct http_req_rule {
char *name; /* header name */
int name_len; /* header name's length */
struct list fmt; /* log-format compatible
expression */
+ regex_t* re; /* used by replace-header and
modify-header */
} hdr_add; /* args used by "add-header" and
"set-header" */
struct redirect_rule *redir; /* redirect rule or
"http-request redirect" */
int nice; /* nice value for
HTTP_REQ_ACT_SET_NICE */
@@ -440,6 +445,7 @@ struct http_res_rule {
char *name; /* header name */
int name_len; /* header name's length */
struct list fmt; /* log-format compatible
expression */
+ regex_t* re; /* used by replace-header and
modify-header */
} hdr_add; /* args used by "add-header" and
"set-header" */
int nice; /* nice value for
HTTP_RES_ACT_SET_NICE */
int loglevel; /* log-level value for
HTTP_RES_ACT_SET_LOGL */
diff --git a/src/haproxy.c b/src/haproxy.c
index d8d8c61..4028f27 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1202,6 +1202,7 @@ void deinit(void)
free(p->fwdfor_hdr_name);
free_http_req_rules(&p->http_req_rules);
+ free_http_res_rules(&p->http_res_rules);
free(p->task);
pool_destroy2(p->req_cap_pool);
diff --git a/src/proto_http.c b/src/proto_http.c
index bd9b13a..83c2b69 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3176,6 +3176,122 @@ static inline void inet_set_tos(int fd, struct
sockaddr_storage from, int tos)
#endif
}
+static int http_replace_header(regex_t* re, char* dst, uint dst_size, char*
val,
+ const char* rep_str)
+{
+ if (regexec(re, val, MAX_MATCH, pmatch, 0))
+ return 0;
+
+ return exp_replace(dst, dst_size, val, rep_str, pmatch);
+}
+
+static int http_modify_header(regex_t* re, char* dst, uint dst_size, char*
val, char delim,
+ const char* rep_str)
+{
+ char* p = val;
+ char* dst_end = dst + dst_size;
+ char* dst_p = dst;
+
+ for (;;) {
+ char *p_delim;
+ const char* tok_end;
+
+ if ((p_delim = (char*)strchr(p, delim))) {
+ *p_delim = 0;
+ tok_end = p_delim;
+ } else {
+ tok_end = p + strlen(p);
+ }
+
+ if (regexec(re, p, MAX_MATCH, pmatch, 0) == 0) {
+ int replace_n = exp_replace(dst_p, dst_end - dst_p, p,
rep_str, pmatch);
+
+ if (replace_n < 0)
+ return -1;
+
+ dst_p += replace_n;
+ } else {
+ uint len = tok_end - p;
+
+ if (dst_p + len >= dst_end)
+ return -1;
+
+ memcpy(dst_p, p, len);
+ dst_p += len;
+ }
+
+ if (dst_p >= dst_end)
+ return -1;
+
+ if (p_delim) {
+ *p_delim = delim;
+ *dst_p++ = delim;
+ p = p_delim + 1;
+ } else {
+ *dst_p = 0;
+ break;
+ }
+ }
+
+ return dst_p - dst;
+}
+
+static int http_transform_header(struct session* s, struct http_msg *msg,
const char* name, uint name_len,
+ char* buf, struct hdr_idx* idx, struct list
*fmt, regex_t* re,
+ struct hdr_ctx* ctx, int action)
+{
+ ctx->idx = 0;
+
+ while (http_find_full_header2(name, name_len, buf, idx, ctx)) {
+ struct hdr_idx_elem *hdr = idx->v + ctx->idx;
+ int delta;
+ char* val = (char*)ctx->line + name_len + 2;
+ char* val_end = (char*)ctx->line + hdr->len;
+ char save_val_end = *val_end;
+ char* reg_dst_buf;
+ uint reg_dst_buf_size;
+ int n_replaced;
+
+ *val_end = 0;
+ trash.len = build_logline(s, trash.str, trash.size, fmt);
+
+ if (trash.len >= trash.size - 1)
+ return -1;
+
+ reg_dst_buf = trash.str + trash.len + 1;
+ reg_dst_buf_size = trash.size - trash.len - 1;
+
+ switch (action)
+ {
+ case HTTP_REQ_ACT_MODIFY_HDR:
+ case HTTP_RES_ACT_MODIFY_HDR:
+ n_replaced = http_modify_header(re,
reg_dst_buf, reg_dst_buf_size, val, ';', trash.str);
+ break;
+ case HTTP_REQ_ACT_REPLACE_HDR:
+ case HTTP_RES_ACT_REPLACE_HDR:
+ n_replaced = http_replace_header(re,
reg_dst_buf, reg_dst_buf_size, val, trash.str);
+ break;
+ default: /* impossible */
+ return -1;
+ }
+
+ *val_end = save_val_end;
+
+ if (n_replaced == 0)
+ continue;
+
+ if (n_replaced < 0)
+ return -1;
+
+ delta = buffer_replace2(msg->chn->buf, val, val_end,
reg_dst_buf, n_replaced);
+
+ hdr->len += delta;
+ http_msg_move_end(msg, delta);
+ }
+
+ return 0;
+}
+
/* Executes the http-request rules <rules> for session <s>, proxy <px> and
* transaction <txn>. Returns the verdict of the first rule that prevents
* further processing of the request (auth, deny, ...), and defaults to
@@ -3238,7 +3354,7 @@ http_req_get_intercept_rule(struct proxy *px, struct list
*rules, struct session
txn->status = (txn->flags & TX_USE_PX_CONN) ? 407 : 401;
stream_int_retnclose(&s->si[0], &trash);
session_inc_http_err_ctr(s);
- return HTTP_RULE_RES_ABRT;
+ return HTTP_RULE_RES_BADREQ;
case HTTP_REQ_ACT_REDIR:
if (!http_apply_redirect_rule(rule->arg.redir, s, txn))
@@ -3265,6 +3381,14 @@ http_req_get_intercept_rule(struct proxy *px, struct
list *rules, struct session
s->logs.level = rule->arg.loglevel;
break;
+ case HTTP_REQ_ACT_REPLACE_HDR:
+ case HTTP_REQ_ACT_MODIFY_HDR:
+ if (http_transform_header(s, &txn->req,
rule->arg.hdr_add.name, rule->arg.hdr_add.name_len,
+ txn->req.chn->buf->p,
&txn->hdr_idx, &rule->arg.hdr_add.fmt,
+ rule->arg.hdr_add.re, &ctx,
rule->action))
+ return HTTP_RULE_RES_ABRT;
+ break;
+
case HTTP_REQ_ACT_DEL_HDR:
case HTTP_REQ_ACT_SET_HDR:
ctx.idx = 0;
@@ -3446,6 +3570,14 @@ http_res_get_intercept_rule(struct proxy *px, struct
list *rules, struct session
s->logs.level = rule->arg.loglevel;
break;
+ case HTTP_RES_ACT_MODIFY_HDR:
+ case HTTP_RES_ACT_REPLACE_HDR:
+ if (http_transform_header(s, &txn->rsp,
rule->arg.hdr_add.name, rule->arg.hdr_add.name_len,
+ txn->rsp.chn->buf->p,
&txn->hdr_idx, &rule->arg.hdr_add.fmt,
+ rule->arg.hdr_add.re, &ctx,
rule->action))
+ return NULL; /* note: we should report an error
here */
+ break;
+
case HTTP_RES_ACT_DEL_HDR:
case HTTP_RES_ACT_SET_HDR:
ctx.idx = 0;
@@ -8757,7 +8889,27 @@ void http_reset_txn(struct session *s)
s->rep->analyse_exp = TICK_ETERNITY;
}
-void free_http_req_rules(struct list *r) {
+static inline void free_regex(regex_t* re)
+{
+ if (re) {
+ regfree(re);
+ free(re);
+ }
+}
+
+void free_http_res_rules(struct list *r)
+{
+ struct http_res_rule *tr, *pr;
+
+ list_for_each_entry_safe(pr, tr, r, list) {
+ LIST_DEL(&pr->list);
+ free_regex(pr->arg.hdr_add.re);
+ free(pr);
+ }
+}
+
+void free_http_req_rules(struct list *r)
+{
struct http_req_rule *tr, *pr;
list_for_each_entry_safe(pr, tr, r, list) {
@@ -8765,6 +8917,7 @@ void free_http_req_rules(struct list *r) {
if (pr->action == HTTP_REQ_ACT_AUTH)
free(pr->arg.auth.realm);
+ free_regex(pr->arg.hdr_add.re);
free(pr);
}
}
@@ -8775,6 +8928,7 @@ struct http_req_rule *parse_http_req_cond(const char
**args, const char *file, i
struct http_req_rule *rule;
struct http_req_action_kw *custom = NULL;
int cur_arg;
+ int is_hdr_rule = 0;
rule = (struct http_req_rule*)calloc(1, sizeof(struct http_req_rule));
if (!rule) {
@@ -8884,12 +9038,32 @@ struct http_req_rule *parse_http_req_cond(const char
**args, const char *file, i
else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) +
1) == 0)
goto bad_log_level;
cur_arg++;
- } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0],
"set-header") == 0) {
- rule->action = *args[0] == 'a' ? HTTP_REQ_ACT_ADD_HDR :
HTTP_REQ_ACT_SET_HDR;
+ } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0],
"set-header") == 0 ||
+ strcmp(args[0], "replace-header") == 0 || strcmp(args[0],
"modify-header") == 0) {
+ const char* fmt_arg;
+
+ switch (*args[0]) {
+ case 'a':
+ rule->action = HTTP_REQ_ACT_ADD_HDR;
+ break;
+ case 's':
+ rule->action = HTTP_REQ_ACT_SET_HDR;
+ break;
+ case 'r':
+ rule->action = HTTP_REQ_ACT_REPLACE_HDR;
+ is_hdr_rule = 1;
+ break;
+ case 'm':
+ rule->action = HTTP_REQ_ACT_MODIFY_HDR;
+ is_hdr_rule = 1;
+ break;
+ }
+
cur_arg = 1;
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)) {
+ (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 &&
+ strcmp(args[cur_arg+2], "unless") != 0 && !is_hdr_rule)) {
Alert("parsing [%s:%d]: 'http-request %s' expects
exactly 2 arguments.\n",
file, linenum, args[0]);
goto out_err;
@@ -8900,9 +9074,31 @@ struct http_req_rule *parse_http_req_cond(const char
**args, const char *file, i
LIST_INIT(&rule->arg.hdr_add.fmt);
proxy->conf.args.ctx = ARGC_HRQ;
- parse_logformat_string(args[cur_arg + 1], proxy,
&rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
+ fmt_arg = (is_hdr_rule) ? args[cur_arg + 2] : args[cur_arg + 1];
+
+ if (is_hdr_rule && !*fmt_arg) {
+ Alert("parsing [%s:%d]: 'http-response %s' expects
exactly 3 arguments.\n",
+ file, linenum, args[0]);
+ goto out_err;
+ }
+
+ parse_logformat_string(fmt_arg, proxy, &rule->arg.hdr_add.fmt,
LOG_OPT_HTTP,
(proxy->cap & PR_CAP_FE) ?
SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
file, linenum);
+
+ if (is_hdr_rule) {
+ if (!(rule->arg.hdr_add.re = (regex_t*)calloc(1,
sizeof(regex_t)))) {
+ Alert("parsing [%s:%d]: out of memory.\n",
file, linenum);
+ goto out_err;
+ }
+
+ if (regcomp(rule->arg.hdr_add.re, args[cur_arg + 1],
REG_EXTENDED)) {
+ Alert("parsing [%s:%d] : '%s' : bad regular
expression.\n", file, linenum,
+ args[cur_arg + 1]);
+ goto out_err;
+ }
+ }
+
free(proxy->conf.lfs_file);
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
@@ -8912,7 +9108,8 @@ struct http_req_rule *parse_http_req_cond(const char
**args, const char *file, i
cur_arg = 1;
if (!*args[cur_arg] ||
- (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 &&
strcmp(args[cur_arg+1], "unless") != 0)) {
+ (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 &&
+ strcmp(args[cur_arg+1], "unless") != 0 && !is_hdr_rule)) {
Alert("parsing [%s:%d]: 'http-request %s' expects
exactly 1 argument.\n",
file, linenum, args[0]);
goto out_err;
@@ -9090,7 +9287,7 @@ struct http_req_rule *parse_http_req_cond(const char
**args, const char *file, i
}
rule->cond = cond;
}
- else if (*args[cur_arg]) {
+ else if (*args[cur_arg] && !is_hdr_rule) {
Alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for
'auth' or"
" either 'if' or 'unless' followed by a condition but
found '%s'.\n",
file, linenum, args[0], args[cur_arg]);
@@ -9109,6 +9306,7 @@ struct http_res_rule *parse_http_res_cond(const char
**args, const char *file, i
struct http_res_rule *rule;
struct http_res_action_kw *custom = NULL;
int cur_arg;
+ int is_hdr_rule;
rule = calloc(1, sizeof(*rule));
if (!rule) {
@@ -9203,12 +9401,32 @@ struct http_res_rule *parse_http_res_cond(const char
**args, const char *file, i
else if ((rule->arg.loglevel = get_log_level(args[cur_arg] +
1)) == 0)
goto bad_log_level;
cur_arg++;
- } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0],
"set-header") == 0) {
- rule->action = *args[0] == 'a' ? HTTP_RES_ACT_ADD_HDR :
HTTP_RES_ACT_SET_HDR;
+ } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0],
"set-header") == 0 ||
+ strcmp(args[0], "replace-header") == 0 || strcmp(args[0],
"modify-header") == 0) {
+ const char* fmt_arg;
+
+ switch (*args[0]) {
+ case 'a':
+ rule->action = HTTP_RES_ACT_ADD_HDR;
+ break;
+ case 's':
+ rule->action = HTTP_RES_ACT_SET_HDR;
+ break;
+ case 'r':
+ rule->action = HTTP_RES_ACT_REPLACE_HDR;
+ is_hdr_rule = 1;
+ break;
+ case 'm':
+ rule->action = HTTP_RES_ACT_MODIFY_HDR;
+ is_hdr_rule = 1;
+ break;
+ }
+
cur_arg = 1;
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)) {
+ (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 &&
+ strcmp(args[cur_arg+2], "unless") != 0 && !is_hdr_rule)) {
Alert("parsing [%s:%d]: 'http-response %s' expects
exactly 2 arguments.\n",
file, linenum, args[0]);
goto out_err;
@@ -9219,9 +9437,35 @@ struct http_res_rule *parse_http_res_cond(const char
**args, const char *file, i
LIST_INIT(&rule->arg.hdr_add.fmt);
proxy->conf.args.ctx = ARGC_HRS;
- parse_logformat_string(args[cur_arg + 1], proxy,
&rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
- (proxy->cap & PR_CAP_BE) ?
SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
- file, linenum);
+ fmt_arg = (is_hdr_rule) ? args[cur_arg + 2] : args[cur_arg + 1];
+
+ if (is_hdr_rule && !*fmt_arg) {
+ Alert("parsing [%s:%d]: 'http-response %s' expects
exactly 3 arguments.\n",
+ file, linenum, args[0]);
+ goto out_err;
+ }
+
+ parse_logformat_string(fmt_arg, proxy, &rule->arg.hdr_add.fmt,
LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_BE) ?
SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
+ file, linenum);
+
+ if (is_hdr_rule) {
+ if (!(rule->arg.hdr_add.re = (regex_t*)calloc(1,
sizeof(regex_t)))) {
+ Alert("parsing [%s:%d]: out of memory.\n",
file, linenum);
+ goto out_err;
+ }
+
+ if (regcomp(rule->arg.hdr_add.re, args[cur_arg + 1],
REG_EXTENDED)) {
+ Alert("parsing [%s:%d] : '%s' : bad regular
expression.\n", file, linenum,
+ args[cur_arg + 1]);
+ goto out_err;
+ }
+
+ parse_logformat_string(args[cur_arg + 2], proxy,
&rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
+ (proxy->cap & PR_CAP_BE) ?
SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
+ file, linenum);
+ }
+
free(proxy->conf.lfs_file);
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
@@ -9393,7 +9637,7 @@ struct http_res_rule *parse_http_res_cond(const char
**args, const char *file, i
}
rule->cond = cond;
}
- else if (*args[cur_arg]) {
+ else if (*args[cur_arg] && !is_hdr_rule) {
Alert("parsing [%s:%d]: 'http-response %s' expects"
" either 'if' or 'unless' followed by a condition but
found '%s'.\n",
file, linenum, args[0], args[cur_arg]);
--
1.7.12.1