http-response set-status 503 reason "Slow Down"
http-after-response set-var(<var-name>) <expr> [ { if | unless } <condition> ]
-
+http-after-response set-var-fmt(<var-name>) <fmt> [ { if | unless } <condition> ]
This is used to set the contents of a variable. The variable is declared
inline.
<expr> Is a standard HAProxy expression formed by a sample-fetch
followed by some converters.
- Example:
- http-after-response set-var(sess.last_redir) res.hdr(location)
+ <fmt> This is the value expressed using log-format rules (see Custom
+ Log Format in section 8.2.4).
+
+ Examples:
+ http-after-response set-var(sess.last_redir) res.hdr(location)
+ http-after-response set-var-fmt(sess.last_be_addr) %[bi]:%[bp]
http-after-response strict-mode { on | off }
http-check set-var(<var-name>) <expr>
+http-check set-var-fmt(<var-name>) <fmt>
This operation sets the content of a variable. The variable is declared inline.
May be used in sections: defaults | frontend | listen | backend
yes | no | yes | yes
<expr> Is a sample-fetch expression potentially followed by converters.
+ <fmt> This is the value expressed using log-format rules (see Custom
+ Log Format in section 8.2.4).
+
Examples :
http-check set-var(check.port) int(1234)
+ http-check set-var-fmt(check.port) "name=%H"
http-check unset-var(<var-name>)
See also "http-request set-path" and "http-request set-query".
http-request set-var(<var-name>) <expr> [ { if | unless } <condition> ]
+http-request set-var-fmt(<var-name>) <fmt> [ { if | unless } <condition> ]
This is used to set the contents of a variable. The variable is declared
inline.
<expr> Is a standard HAProxy expression formed by a sample-fetch
followed by some converters.
+ <fmt> This is the value expressed using log-format rules (see Custom
+ Log Format in section 8.2.4).
+
Example:
http-request set-var(req.my_var) req.fhdr(user-agent),lower
+ http-request set-var-fmt(txn.from) %[src]:%[src_port]
+
http-request send-spoe-group <engine-name> <group-name>
[ { if | unless } <condition> ]
See RFC 2474, 2597, 3260 and 4594 for more information.
http-response set-var(<var-name>) <expr> [ { if | unless } <condition> ]
+http-response set-var-fmt(<var-name>) <fmt> [ { if | unless } <condition> ]
This is used to set the contents of a variable. The variable is declared
inline.
<expr> Is a standard HAProxy expression formed by a sample-fetch
followed by some converters.
- Example:
- http-response set-var(sess.last_redir) res.hdr(location)
+ <fmt> This is the value expressed using log-format rules (see Custom
+ Log Format in section 8.2.4).
+
+ Examples:
+ http-response set-var(sess.last_redir) res.hdr(location)
+ http-response set-var-fmt(sess.last_be_addr) %[bi]:%[bp]
http-response silent-drop [ { if | unless } <condition> ]
tcp-check set-var(<var-name>) <expr>
+tcp-check set-var-fmt(<var-name>) <fmt>
This operation sets the content of a variable. The variable is declared inline.
May be used in sections: defaults | frontend | listen | backend
yes | no | yes | yes
<expr> Is a sample-fetch expression potentially followed by converters.
+ <fmt> This is the value expressed using log-format rules (see Custom
+ Log Format in section 8.2.4).
+
Examples :
tcp-check set-var(check.port) int(1234)
+ tcp-check set-var-fmt(check.name) "%H"
tcp-check unset-var(<var-name>)
- set-nice <nice>
- set-tos <tos>
- set-var(<var-name>) <expr>
+ - set-var-fmt(<var-name>) <fmt>
- switch-mode http [ proto <name> ]
- unset-var(<var-name>)
- silent-drop
The "set-tos" is used to set the TOS or DSCP field value of packets sent to
the client. More information on how to use it at "http-request set-tos".
- The "set-var" is used to set the content of a variable. The variable is
- declared inline. For "tcp-request session" rules, only session-level
- variables can be used, without any layer7 contents.
+ The "set-var" and "set-var-fmt" are used to set the contents of a variable.
+ The variable is declared inline. For "tcp-request session" rules, only
+ session-level variables can be used, without any layer7 contents. The
+ "set-var" action takes a regular expression while "set-var-fmt" takes a
+ format string.
<var-name> The name of the variable starts with an indication about
its scope. The scopes allowed are:
<expr> Is a standard HAProxy expression formed by a sample-fetch
followed by some converters.
+ <fmt> This is the value expressed using log-format rules (see Custom
+ Log Format in section 8.2.4).
+
The "switch-mode" is used to perform a connection upgrade. Only HTTP
upgrades are supported for now. The protocol may optionally be
specified. This action is only available for a proxy with the frontend
Example:
tcp-request content set-var(sess.my_var) src
+ tcp-request content set-var-fmt(sess.from) %[src]:%[src_port]
tcp-request content unset-var(sess.my_var2)
Example:
- set-var(<var-name>) <expr>
Sets a variable from an expression.
+ - set-var-fmt(<var-name>) <fmt>
+ Sets a variable from a log format string.
+
- unset-var(<var-name>)
Unsets a variable.
<expr> Is a standard HAProxy expression formed by a sample-fetch
followed by some converters.
+ <fmt> This is the value expressed using log-format rules (see Custom
+ Log Format in section 8.2.4).
+
The "unset-var" is used to unset a variable. See above for details about
<var-name>.
- set-src-port <expr>
- set-tos <tos>
- set-var(<var-name>) <expr>
+ - set-var-fmt(<var-name>) <fmt>
- unset-var(<var-name>)
- silent-drop
other variables, such as colon-delimited values. If commas or closing
parenthesis are needed as delimiters, they must be protected by quotes or
backslashes, themselves protected so that they are not stripped by the first
- level parser. See examples below.
+ level parser. This is often used to build composite variables from other
+ ones, but sometimes using a format string with multiple fields may be more
+ convenient. See examples below.
Example:
tcp-request session set-var(sess.src) src
tcp-request session set-var(sess.dn) ssl_c_s_dn
tcp-request session set-var(txn.sig) str(),concat(<ip=,sess.ip,>),concat(<dn=,sess.dn,>)
tcp-request session set-var(txn.ipport) "str(),concat('addr=(',sess.ip),concat(',',sess.port,')')"
+ tcp-request session set-var-fmt(txn.ipport) "addr=(%[sess.ip],%[sess.port])" ## does the same
http-request set-header x-hap-sig %[var(txn.sig)]
cpl
varnishtest "Test a few set-var() in global, tcp and http rule sets, at different scopes"
-#REQUIRE_VERSION=2.4
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev5)'"
feature ignore_unknown_macro
global
set-var proc.int12 int(12)
set-var proc.int5 str(60),div(proc.int12)
- set-var proc.str str("this is")
+ set-var proc.str1 str("this is")
+ set-var proc.str2 str("a string")
+ set-var proc.str var(proc.str1)
set-var proc.str str(""),concat("",proc.str," a string")
set-var proc.uuid uuid()
frontend fe1
bind "fd@${fe1}"
+ tcp-request session set-var-fmt(sess.str3) "%[var(proc.str1)] %[var(proc.str2)]"
tcp-request session set-var(sess.int5) var(proc.int5)
tcp-request session set-var(proc.int5) var(proc.int5),add(sess.int5) ## proc. becomes 10
+ tcp-request content set-var-fmt(req.str4) "%[var(sess.str3),regsub(is a,is also a)]"
+ http-request set-var-fmt(txn.str5) "%[var(req.str4)]"
http-request set-var(req.int5) var(sess.int5)
http-request set-var(sess.int5) var(sess.int5),add(req.int5) ## sess. becomes 10 first time, then 15...
- http-request return status 200 hdr x-var "proc=%[var(proc.int5)] sess=%[var(sess.int5)] req=%[var(req.int5)] str=%[var(proc.str)] uuid=%[var(proc.uuid)]"
+ http-request return status 200 hdr x-var "proc=%[var(proc.int5)] sess=%[var(sess.int5)] req=%[var(req.int5)] str=%[var(proc.str)] str5=%[var(txn.str5)] uuid=%[var(proc.uuid)]"
} -start
haproxy h1 -cli {
txreq -req GET -url /req1_1
rxresp
expect resp.status == 200
- expect resp.http.x-var ~ "proc=10 sess=10 req=5 str=this is a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+ expect resp.http.x-var ~ "proc=10 sess=10 req=5 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
txreq -req GET -url /req1_2
rxresp
expect resp.status == 200
- expect resp.http.x-var ~ "proc=10 sess=20 req=10 str=this is a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+ expect resp.http.x-var ~ "proc=10 sess=20 req=10 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
} -run
haproxy h1 -cli {
txreq -req GET -url /req2_1
rxresp
expect resp.status == 200
- expect resp.http.x-var ~ "proc=20 sess=20 req=10 str=this is a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+ expect resp.http.x-var ~ "proc=20 sess=20 req=10 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
txreq -req GET -url /req2_2
rxresp
expect resp.status == 200
- expect resp.http.x-var ~ "proc=20 sess=40 req=20 str=this is a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+ expect resp.http.x-var ~ "proc=20 sess=40 req=20 str=this is a string str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
} -run
haproxy h1 -cli {
txreq -req GET -url /req3_1
rxresp
expect resp.status == 200
- expect resp.http.x-var ~ "proc=40 sess=40 req=20 str=updated uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+ expect resp.http.x-var ~ "proc=40 sess=40 req=20 str=updated str5=this is also a string uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
} -run
static enum act_return action_store(struct act_rule *rule, struct proxy *px,
struct session *sess, struct stream *s, int flags)
{
+ struct buffer *fmtstr = NULL;
struct sample smp;
int dir;
/* Process the expression. */
memset(&smp, 0, sizeof(smp));
- if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL,
- rule->arg.vars.expr, &smp))
- return ACT_RET_CONT;
+
+ if (!LIST_ISEMPTY(&rule->arg.vars.fmt)) {
+ /* a format-string is used */
+
+ fmtstr = alloc_trash_chunk();
+ if (!fmtstr) {
+ send_log(px, LOG_ERR, "Vars: memory allocation failure while processing store rule.");
+ if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
+ ha_alert("Vars: memory allocation failure while processing store rule.\n");
+ return ACT_RET_CONT;
+ }
+
+ /* execute the log-format expression */
+ fmtstr->data = sess_build_logline(sess, s, fmtstr->area, fmtstr->size, &rule->arg.vars.fmt);
+
+ /* convert it to a sample of type string as it's what the vars
+ * API consumes, and store it.
+ */
+ smp_set_owner(&smp, px, sess, s, 0);
+ smp.data.type = SMP_T_STR;
+ smp.data.u.str = *fmtstr;
+ sample_store_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
+ }
+ else {
+ /* an expression is used */
+ if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL,
+ rule->arg.vars.expr, &smp))
+ return ACT_RET_CONT;
+ }
/* Store the sample, and ignore errors. */
sample_store_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
+ free_trash_chunk(fmtstr);
return ACT_RET_CONT;
}
static void release_store_rule(struct act_rule *rule)
{
+ struct logformat_node *lf, *lfb;
+ list_for_each_entry_safe(lf, lfb, &rule->arg.http.fmt, list) {
+ LIST_DELETE(&lf->list);
+ release_sample_expr(lf->expr);
+ free(lf->arg);
+ free(lf);
+ }
+
release_sample_expr(rule->arg.vars.expr);
}
/* This function is a common parser for using variables. It understands
* the format:
*
+ * set-var-fmt(<variable-name>) <format-string>
* set-var(<variable-name>) <expression>
* unset-var(<variable-name>)
*
const char *var_name = args[*arg-1];
int var_len;
const char *kw_name;
- int flags, set_var = 0;
+ int flags, set_var = 0; /* 0=unset-var, 1=set-var, 2=set-var-fmt */
- if (strncmp(var_name, "set-var", 7) == 0) {
+ if (strncmp(var_name, "set-var-fmt", 11) == 0) {
+ var_name += 11;
+ set_var = 2;
+ }
+ else if (strncmp(var_name, "set-var", 7) == 0) {
var_name += 7;
set_var = 1;
}
}
if (*var_name != '(') {
- memprintf(err, "invalid or incomplete action '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
+ memprintf(err, "invalid or incomplete action '%s'. Expects 'set-var(<var-name>)', 'set-var-fmt(<var-name>)' or 'unset-var(<var-name>)'",
args[*arg-1]);
return ACT_RET_PRS_ERR;
}
var_len = strlen(var_name);
var_len--; /* remove the ')' */
if (var_name[var_len] != ')') {
- memprintf(err, "incomplete expression after action '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
+ memprintf(err, "incomplete argument after action '%s'. Expects 'set-var(<var-name>)', 'set-var-fmt(<var-name>)' or 'unset-var(<var-name>)'",
args[*arg-1]);
return ACT_RET_PRS_ERR;
}
+ LIST_INIT(&rule->arg.vars.fmt);
rule->arg.vars.name = register_name(var_name, var_len, &rule->arg.vars.scope, 1, err);
if (!rule->arg.vars.name)
return ACT_RET_PRS_ERR;
return ACT_RET_PRS_ERR;
}
- rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
- px->conf.args.line, err, &px->conf.args, NULL);
- if (!rule->arg.vars.expr)
- return ACT_RET_PRS_ERR;
+ if (set_var == 2) { /* set-var-fmt */
+ if (!parse_logformat_string(args[*arg], px, &rule->arg.vars.fmt, 0, flags, err))
+ return ACT_RET_PRS_ERR;
- if (!(rule->arg.vars.expr->fetch->val & flags)) {
- memprintf(err,
- "fetch method '%s' extracts information from '%s', none of which is available here",
- kw_name, sample_src_names(rule->arg.vars.expr->fetch->use));
- free(rule->arg.vars.expr);
- return ACT_RET_PRS_ERR;
+ (*arg)++;
+
+ /* for late error reporting */
+ free(px->conf.lfs_file);
+ px->conf.lfs_file = strdup(px->conf.args.file);
+ px->conf.lfs_line = px->conf.args.line;
+ } else {
+ /* set-var */
+ rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
+ px->conf.args.line, err, &px->conf.args, NULL);
+ if (!rule->arg.vars.expr)
+ return ACT_RET_PRS_ERR;
+
+ if (!(rule->arg.vars.expr->fetch->val & flags)) {
+ memprintf(err,
+ "fetch method '%s' extracts information from '%s', none of which is available here",
+ kw_name, sample_src_names(rule->arg.vars.expr->fetch->use));
+ free(rule->arg.vars.expr);
+ return ACT_RET_PRS_ERR;
+ }
}
rule->action = ACT_CUSTOM;
INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
static struct action_kw_list tcp_req_sess_kws = { { }, {
+ { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
{ "set-var", parse_store, KWF_MATCH_PREFIX },
{ "unset-var", parse_store, KWF_MATCH_PREFIX },
{ /* END */ }
INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_kws);
static struct action_kw_list tcp_req_cont_kws = { { }, {
+ { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
{ "set-var", parse_store, KWF_MATCH_PREFIX },
{ "unset-var", parse_store, KWF_MATCH_PREFIX },
{ /* END */ }
INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_kws);
static struct action_kw_list tcp_res_kws = { { }, {
+ { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
{ "set-var", parse_store, KWF_MATCH_PREFIX },
{ "unset-var", parse_store, KWF_MATCH_PREFIX },
{ /* END */ }
INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_kws);
static struct action_kw_list tcp_check_kws = {ILH, {
+ { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
{ "set-var", parse_store, KWF_MATCH_PREFIX },
{ "unset-var", parse_store, KWF_MATCH_PREFIX },
{ /* END */ }
INITCALL1(STG_REGISTER, tcp_check_keywords_register, &tcp_check_kws);
static struct action_kw_list http_req_kws = { { }, {
+ { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
{ "set-var", parse_store, KWF_MATCH_PREFIX },
{ "unset-var", parse_store, KWF_MATCH_PREFIX },
{ /* END */ }
INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_kws);
static struct action_kw_list http_res_kws = { { }, {
+ { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
{ "set-var", parse_store, KWF_MATCH_PREFIX },
{ "unset-var", parse_store, KWF_MATCH_PREFIX },
{ /* END */ }
INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_kws);
static struct action_kw_list http_after_res_kws = { { }, {
+ { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
{ "set-var", parse_store, KWF_MATCH_PREFIX },
{ "unset-var", parse_store, KWF_MATCH_PREFIX },
{ /* END */ }