MINOR: http-ana: Add support for "set-cookie-fmt" option to redirect rules

It is now possible to use a log-format string to define the "Set-Cookie"
header value of a response generated by a redirect rule. There is no special
check on the result format and it is not possible during the configuration
parsing. It is proably not a big deal because already existing "set-cookie"
and "clear-cookie" options don't perform any check.

Here is an example:

  http-request redirect location https://someurl.com/ set-cookie haproxy="%[var(txn.var)]"

This patch should fix the issue #1784.
This commit is contained in:
Christopher Faulet 2024-11-18 18:03:23 +01:00
parent b2877db47c
commit 1be7140ade
5 changed files with 63 additions and 19 deletions

View File

@ -11453,6 +11453,12 @@ redirect scheme <sch> [code <code>] <option> [{if | unless} <condition>]
that for a browser, a sole cookie name without an equal sign is
different from a cookie with an equal sign.
- "set-cookie-fmt <fmt>"
It is equivaliant to the option above, except the "Set-Cookie" header
will be filled with the result of the log-format string <fmt>
evaluation. Be carefull to respect the "NAME[=value]" format because no
special check are performed during the configuration parsing.
- "clear-cookie NAME[=]"
A "Set-Cookie" header will be added with NAME (and optionally "="), but
with the "Max-Age" attribute set to zero. This will tell the browser to

View File

@ -168,6 +168,7 @@ enum {
REDIRECT_FLAG_FROM_REQ = 4, /* redirect rule on the request path */
REDIRECT_FLAG_IGNORE_EMPTY = 8, /* silently ignore empty location expressions */
REDIRECT_FLAG_KEEP_QS = 16, /* append the query string to location, if any */
REDIRECT_FLAG_COOKIE_FMT = 32, /* The cookie value is a log-format stirng*/
};
/* Redirect types (location, prefix, extended ) */

View File

@ -518,8 +518,10 @@ struct redirect_rule {
struct lf_expr rdr_fmt;
int code;
unsigned int flags;
int cookie_len;
char *cookie_str;
union {
struct ist str; /* the cookie is a string */
struct lf_expr fmt; /* or a log-format string (possible for set-cookie only) */
} cookie;
};
/* some of the most common options which are also the easiest to handle */

View File

@ -2518,8 +2518,13 @@ int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struc
goto fail;
}
if (rule->cookie_len) {
if (!htx_add_header(htx, ist("Set-Cookie"), ist2(rule->cookie_str, rule->cookie_len)))
if (rule->flags & REDIRECT_FLAG_COOKIE_FMT) {
trash.data = build_logline(s, trash.area, trash.size, &rule->cookie.fmt);
if (!htx_add_header(htx, ist("Set-Cookie"), ist2(trash.area, trash.data)))
goto fail;
}
else if (isttest(rule->cookie.str)) {
if (!htx_add_header(htx, ist("Set-Cookie"), rule->cookie.str))
goto fail;
}

View File

@ -322,7 +322,10 @@ void http_free_redirect_rule(struct redirect_rule *rdr)
{
free_acl_cond(rdr->cond);
free(rdr->rdr_str);
free(rdr->cookie_str);
if ((rdr->flags & REDIRECT_FLAG_COOKIE_FMT))
lf_expr_deinit(&rdr->cookie.fmt);
else
istfree(&rdr->cookie.str);
lf_expr_deinit(&rdr->rdr_fmt);
free(rdr);
}
@ -342,6 +345,7 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
const char *destination = NULL;
const char *cookie = NULL;
int cookie_set = 0;
size_t cookie_len = 0;
unsigned int flags = (!dir ? REDIRECT_FLAG_FROM_REQ : REDIRECT_FLAG_NONE);
struct acl_cond *cond = NULL;
@ -378,6 +382,14 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
cookie = args[cur_arg];
cookie_set = 1;
}
else if (strcmp(args[cur_arg], "set-cookie-fmt") == 0) {
if (!*args[cur_arg + 1])
goto missing_arg;
cur_arg++;
cookie = args[cur_arg];
cookie_set = 2;
}
else if (strcmp(args[cur_arg], "clear-cookie") == 0) {
if (!*args[cur_arg + 1])
goto missing_arg;
@ -422,7 +434,8 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
}
else {
memprintf(errmsg,
"expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query', 'keep-query', 'ignore-empty' or 'append-slash' (was '%s')",
"expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'set-cookie-fmt',"
" 'clear-cookie', 'drop-query', 'keep-query', 'ignore-empty' or 'append-slash' (was '%s')",
args[cur_arg]);
goto err;
}
@ -476,21 +489,38 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
/* depending on cookie_set, either we want to set the cookie, or to clear it.
* a clear consists in appending "; path=/; Max-Age=0;" at the end.
*/
rule->cookie_len = strlen(cookie);
if (cookie_set) {
rule->cookie_str = malloc(rule->cookie_len + 10);
if (!rule->cookie_str)
cookie_len = strlen(cookie);
if (cookie_set == 1) { // set-cookie
rule->cookie.str = istalloc(cookie_len+9);
if (!isttest(rule->cookie.str))
goto out_of_memory;
memcpy(rule->cookie_str, cookie, rule->cookie_len);
memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10);
rule->cookie_len += 9;
} else {
rule->cookie_str = malloc(rule->cookie_len + 21);
if (!rule->cookie_str)
istcpy(&rule->cookie.str, ist2(cookie, cookie_len), cookie_len);
istcat(&rule->cookie.str, ist2("; path=/;", 9), cookie_len+10);
}
else if (cookie_set == 2) { // set-cookie-fmt
int cap = 0;
lf_expr_init(&rule->cookie.fmt);
curproxy->conf.args.ctx = ARGC_RDR;
if (curproxy->cap & PR_CAP_FE)
cap |= (dir ? SMP_VAL_FE_HRS_HDR : SMP_VAL_FE_HRQ_HDR);
if (curproxy->cap & PR_CAP_BE)
cap |= (dir ? SMP_VAL_BE_HRS_HDR : SMP_VAL_BE_HRQ_HDR);
chunk_memcpy(&trash, cookie, cookie_len);
chunk_strcat(&trash, "; path=/;");
if (!parse_logformat_string(trash.area, curproxy, &rule->cookie.fmt, LOG_OPT_HTTP, cap, errmsg)) {
goto err;
}
flags |= REDIRECT_FLAG_COOKIE_FMT;
}
else { // clear-cookie
rule->cookie.str = istalloc(cookie_len+20);
if (!isttest(rule->cookie.str))
goto out_of_memory;
memcpy(rule->cookie_str, cookie, rule->cookie_len);
memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21);
rule->cookie_len += 20;
istcpy(&rule->cookie.str, ist2(cookie, cookie_len), cookie_len);
istcat(&rule->cookie.str, ist2("; path=/; Max-Age=0;", 20), cookie_len+21);
}
}
rule->type = type;