]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: http_act: define set-timeout server/tunnel action
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 10 Dec 2020 12:43:54 +0000 (13:43 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 11 Dec 2020 11:01:07 +0000 (12:01 +0100)
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.

doc/configuration.txt
include/haproxy/action-t.h
include/haproxy/action.h
src/action.c
src/http_act.c

index 81a90daf9682e84b7a9947c2f014af5f4af09fc6..d4bfd1c65f221b6c8835465ac3934027ff477d43 100644 (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
index 73a846f1ce14f960545d0c5c1a3be906de865c8b..2ea524db29370afbc9fbff3f4204a837b87323b2 100644 (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;
index c7ce92dc5f37e1c0ca66bc979593f2fba8f88074..c5c0a6c4bc52241f083756b8b66d9a5fc9f7cda0 100644 (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 */
index 870a8393ae7e6d6a65f2c797f96e3a1f4831065d..c4c263faf729e6b5a2ab5e3b015c4e9ddd406735 100644 (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;
+}
index 85a534e8d2401d5d59ea37fcc61ad2018b6f2ab8..140cdf1a9f0ec4e862a4751bce547df9ef31ba38 100644 (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 }
        }
 };