]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: http-act: Add 'pause' action to temporarily suspend the message analysis
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 22 Apr 2025 14:08:50 +0000 (16:08 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 22 Apr 2025 14:14:47 +0000 (16:14 +0200)
The 'pause' HTTP action can now be used to suspend for a moment the message
analysis. A timeout, expressed in milliseconds using a time-format
parameter, or an expression can be used. If an expression is used, errors
and invalid values are ignored.

Internally, the action will set the analysis expiration date on the
corresponding channel to the configured value and it will yield while it is
not expired.

The 'pause' action is available for 'http-request' and 'http-response'
rules.

doc/configuration.txt
src/http_act.c

index c8ec013b751ef0ca06c98359a747e42353398e06..80577890f75c676ecfe823cdf14dfd8f3b68239f 100644 (file)
@@ -15537,6 +15537,7 @@ early-hint                     -           -     -     -     -            X   -
 expect-netscaler-cip           -           X     -     -     -            -   -   -
 expect-proxy layer4            -           X     -     -     -            -   -   -
 normalize-uri                  -           -     -     -     -            X   -   -
+pause                          -           -     -     -     -            X   X   -
 redirect                       -           -     -     -     -            X   X   -
 reject                         X           X     X     X     X            X   -   -
 replace-header                 -           -     -     -     -            X   X   X
@@ -16107,6 +16108,22 @@ normalize-uri query-sort-by-name
       - /?aaa=3&a=1&aa=2      -> /?a=1&aa=2&aaa=3
       - /?a=3&b=4&a=1&b=5&a=2 -> /?a=3&a=1&a=2&b=4&b=5
 
+pause { <timeout> | <expr> }
+  Usable in:  QUIC Ini|    TCP RqCon| RqSes| RqCnt| RsCnt|    HTTP Req| Res| Aft
+                    - |          -  |   -  |   -  |   -  |          X |  X |  -
+
+  This suspends the message analysis for the sepcified number of milliseconds.
+  The timeout can be specified in milliseconds 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 return a number
+  interpreted as a timeout in milliseconds. If the expression evaluation fails
+  or if it returns an invalid value, the action is ignored and the evaluation
+  continues.
+
+  This action may be used for debugging purpose. But it could also be used to
+  slow down some clients based on specific criteria. For instance, it is
+  possible to slow down clients if their requests rate is too high, by tracking
+  them via a "track-sc" rule.
 
 redirect <rule>
   Usable in:  QUIC Ini|    TCP RqCon| RqSes| RqCnt| RsCnt|    HTTP Req| Res| Aft
index 0bb61651887190ed30505777a64e09fde30585d9..ae435da64e904f59dfe930ae3d85ad2e49a0062f 100644 (file)
@@ -1752,6 +1752,82 @@ static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg
        return ACT_RET_PRS_OK;
 }
 
+/* This function executes a pause action.
+ */
+static enum act_return http_action_pause(struct act_rule *rule, struct proxy *px,
+                                        struct session *sess, struct stream *s, int flags)
+{
+
+       struct channel *chn = ((rule->from == ACT_F_HTTP_REQ) ? &s->req : &s->res);
+       struct sample *key;
+
+       if (!tick_isset(chn->analyse_exp)) {
+               int time;
+
+               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;
+                       time = MS_TO_TICKS(key->data.u.sint);
+               }
+               else
+                       time  = MS_TO_TICKS(rule->arg.timeout.value);
+               chn->analyse_exp = tick_add_ifset(now_ms, time);
+       }
+
+       if (tick_isset(chn->analyse_exp) && !tick_is_expired(chn->analyse_exp, now_ms))
+               return ACT_RET_YIELD;
+
+       chn->analyse_exp = TICK_ETERNITY;
+       return ACT_RET_CONT;
+}
+
+/* Parse a "pause" action. It returns ACT_RET_PRS_OK on success,
+ * ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_pause(const char **args, int *orig_arg, struct proxy *px,
+                                             struct act_rule *rule, char **err)
+{
+       const char *res;
+       int cur_arg;
+
+       rule->action = ACT_CUSTOM;
+       rule->action_ptr = http_action_pause;
+       rule->release_ptr = release_timeout_action;
+
+       cur_arg = *orig_arg;
+
+       res = parse_time_err(args[cur_arg], (unsigned int *)&rule->arg.timeout.value, TIME_UNIT_MS);
+        if (res == PARSE_TIME_OVER) {
+                memprintf(err, "timer overflow in argument '%s' to rule 'pause' (maximum value is 2147483647 ms or ~24.8 days)",
+                          args[cur_arg]);
+                return ACT_RET_PRS_ERR;
+        }
+        else if (res == PARSE_TIME_UNDER) {
+                memprintf(err, "timer underflow in argument '%s' to rule 'pause' (minimum value is 1 ms)",
+                          args[cur_arg]);
+                return ACT_RET_PRS_ERR;
+        }
+        /* res not NULL, parsing error */
+        else if (res) {
+                rule->arg.timeout.expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file,
+                                                           px->conf.args.line, err, &px->conf.args, NULL);
+                if (!rule->arg.timeout.expr) {
+                        memprintf(err, "unexpected character '%c' in rule 'mause'", *res);
+                        return ACT_RET_PRS_ERR;
+                }
+        }
+        /* res NULL, parsing ok but value is 0 */
+        else if (!(rule->arg.timeout.value)) {
+                memprintf(err, "null value is not valid for a 'pause' rule");
+                return ACT_RET_PRS_ERR;
+        }
+
+       *orig_arg = cur_arg + 1;
+       return ACT_RET_PRS_OK;
+}
+
+
 /* Release memory allocated by an http redirect action. */
 static void release_http_redir(struct act_rule *rule)
 {
@@ -2424,6 +2500,7 @@ static struct action_kw_list http_req_actions = {
                { "do-log",           parse_http_req_do_log,           0 },
                { "early-hint",       parse_http_set_header,           0 },
                { "normalize-uri",    parse_http_normalize_uri,        KWF_EXPERIMENTAL },
+               { "pause",            parse_http_pause,                0 },
                { "redirect",         parse_http_redirect,             0 },
                { "reject",           parse_http_action_reject,        0 },
                { "replace-header",   parse_http_replace_header,       0 },
@@ -2461,6 +2538,7 @@ static struct action_kw_list http_res_actions = {
                { "del-map",         parse_http_set_map,        KWF_MATCH_PREFIX },
                { "deny",            parse_http_deny,           0 },
                { "do-log",          parse_http_res_do_log,     0 },
+               { "pause",           parse_http_pause,          0 },
                { "redirect",        parse_http_redirect,       0 },
                { "replace-header",  parse_http_replace_header, 0 },
                { "replace-value",   parse_http_replace_header, 0 },