MEDIUM: http_act: define set-timeout server/tunnel action

Add a new http-request action 'set-timeout [server/tunnel]'. This action
can be used to update the server or tunnel timeout of a stream. It takes
two parameters, the timeout name to update and the new timeout value.
This rule is only valid for a proxy with backend capabilities. The
timeout value cannot be null. A sample expression can also be used
instead of a plain value.
This commit is contained in:
Amaury Denoyelle 2020-12-10 13:43:54 +01:00 committed by Christopher Faulet
parent fb50443517
commit 8d22823ade
5 changed files with 153 additions and 0 deletions

View File

@ -6265,6 +6265,24 @@ http-request set-src-port <expr> [ { if | unless } <condition> ]
the address family supports a port, otherwise it forces the source address to
IPv4 "0.0.0.0" before rewriting the port.
http-request set-timeout server|tunnel { <timeout> | <expr> }
[ { if | unless } <condition> ]
This action overrides the specified "server" or "tunnel" timeout for the
current stream only. The timeout can be specified in millisecond or with any
other unit if the number is suffixed by the unit as explained at the top of
this document. It is also possible to write an expression which must returns
a number interpreted as a timeout in millisecond.
Note that the server/tunnel timeouts are only relevant on the backend side
and thus this rule is only available for the proxies with backend
capabilities. Also the timeout value must be non-null to obtain the expected
results.
Example:
http-request set-timeout server 5s
http-request set-timeout hdr(host),map_int(host.lst)
http-request set-tos <tos> [ { if | unless } <condition> ]
This is used to set the TOS or DSCP field value of packets sent to the client

View File

@ -143,6 +143,11 @@ struct act_rule {
struct sample_expr *expr;
int idx;
} capid;
struct {
int value; /* plain timeout value in ms if no expr is used */
enum act_timeout_name type; /* timeout type */
struct sample_expr *expr; /* timeout value as an expression */
} timeout;
struct hlua_rule *hlua_rule;
struct {
struct sample_expr *expr;

View File

@ -25,6 +25,7 @@
#include <stdio.h>
#include <haproxy/action-t.h>
#include <haproxy/list.h>
#include <haproxy/sample.h>
int act_resolution_cb(struct dns_requester *requester, struct dns_nameserver *nameserver);
int act_resolution_error_cb(struct dns_requester *requester, int error_code);
@ -90,4 +91,14 @@ int check_trk_action(struct act_rule *rule, struct proxy *px, char **err);
*/
int check_capture(struct act_rule *rule, struct proxy *px, char **err);
int cfg_parse_rule_set_timeout(const char **args, int idx, int *out_timeout,
enum act_timeout_name *name,
struct sample_expr **expr, char **err,
const char *file, int line, struct arg_list *al);
static inline void release_timeout_action(struct act_rule *rule)
{
release_sample_expr(rule->arg.timeout.expr);
}
#endif /* _HAPROXY_ACTION_H */

View File

@ -149,3 +149,60 @@ int act_resolution_error_cb(struct dns_requester *requester, int error_code)
return 0;
}
/* Parse a set-timeout rule statement. It first checks if the timeout name is
* valid and returns it in <name>. Then the timeout is parsed as a plain value
* and * returned in <out_timeout>. If there is a parsing error, the value is
* reparsed as an expression and returned in <expr>.
*
* Returns -1 if the name is invalid or neither a time or an expression can be
* parsed, or if the timeout value is 0.
*/
int cfg_parse_rule_set_timeout(const char **args, int idx, int *out_timeout,
enum act_timeout_name *name,
struct sample_expr **expr, char **err,
const char *file, int line, struct arg_list *al)
{
const char *res;
const char *timeout_name = args[idx++];
if (!strcmp(timeout_name, "server")) {
*name = ACT_TIMEOUT_SERVER;
}
else if (!strcmp(timeout_name, "tunnel")) {
*name = ACT_TIMEOUT_TUNNEL;
}
else {
memprintf(err,
"'set-timeout' rule supports 'server'/'tunnel' (got '%s')",
timeout_name);
return -1;
}
res = parse_time_err(args[idx], (unsigned int *)out_timeout, TIME_UNIT_MS);
if (res == PARSE_TIME_OVER) {
memprintf(err, "timer overflow in argument '%s' to rule 'set-timeout %s' (maximum value is 2147483647 ms or ~24.8 days)",
args[idx], timeout_name);
return -1;
}
else if (res == PARSE_TIME_UNDER) {
memprintf(err, "timer underflow in argument '%s' to rule 'set-timeout %s' (minimum value is 1 ms)",
args[idx], timeout_name);
return -1;
}
/* res not NULL, parsing error */
else if (res) {
*expr = sample_parse_expr((char **)args, &idx, file, line, err, al, NULL);
if (!*expr) {
memprintf(err, "unexpected character '%c' in rule 'set-timeout %s'", *res, timeout_name);
return -1;
}
}
/* res NULL, parsing ok but value is 0 */
else if (!(*out_timeout)) {
memprintf(err, "null value is not valid for a 'set-timeout %s' rule",
timeout_name);
return -1;
}
return 0;
}

View File

@ -1901,6 +1901,67 @@ static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg,
return ACT_RET_PRS_OK;
}
static enum act_return action_timeout_set_stream_timeout(struct act_rule *rule,
struct proxy *px,
struct session *sess,
struct stream *s,
int flags)
{
struct sample *key;
if (rule->arg.timeout.expr) {
key = sample_fetch_as_type(px, sess, s, SMP_OPT_FINAL, rule->arg.timeout.expr, SMP_T_SINT);
if (!key)
return ACT_RET_CONT;
stream_set_timeout(s, rule->arg.timeout.type, MS_TO_TICKS(key->data.u.sint));
}
else {
stream_set_timeout(s, rule->arg.timeout.type, MS_TO_TICKS(rule->arg.timeout.value));
}
return ACT_RET_CONT;
}
/* Parse a "set-timeout" action. Returns ACT_RET_PRS_ERR if parsing error.
*/
static enum act_parse_ret parse_http_set_timeout(const char **args,
int *orig_arg,
struct proxy *px,
struct act_rule *rule, char **err)
{
int cur_arg;
rule->action = ACT_CUSTOM;
rule->action_ptr = action_timeout_set_stream_timeout;
rule->release_ptr = release_timeout_action;
cur_arg = *orig_arg;
if (!*args[cur_arg] || !*args[cur_arg + 1]) {
memprintf(err, "expects exactly 2 arguments");
return ACT_RET_PRS_ERR;
}
if (!(px->cap & PR_CAP_BE)) {
memprintf(err, "proxy '%s' has no backend capability", px->id);
return ACT_RET_PRS_ERR;
}
if (cfg_parse_rule_set_timeout(args, cur_arg,
&rule->arg.timeout.value,
&rule->arg.timeout.type,
&rule->arg.timeout.expr,
err,
px->conf.args.file,
px->conf.args.line, &px->conf.args) == -1) {
return ACT_RET_PRS_ERR;
}
*orig_arg = cur_arg + 2;
return ACT_RET_PRS_OK;
}
/* This function executes a strict-mode actions. On success, it always returns
* ACT_RET_CONT
*/
@ -2034,6 +2095,7 @@ static struct action_kw_list http_req_actions = {
{ "strict-mode", parse_http_strict_mode, 0 },
{ "tarpit", parse_http_deny, 0 },
{ "track-sc", parse_http_track_sc, 1 },
{ "set-timeout", parse_http_set_timeout, 0 },
{ NULL, NULL }
}
};