]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: Add connect/queue/tarpit timeouts to set-timeout
authorNenad Merdanovic <nenad.merdanovic@cachefly.com>
Wed, 18 Feb 2026 14:56:36 +0000 (15:56 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 19 Feb 2026 07:20:37 +0000 (08:20 +0100)
Add the ability to set connect, queue and tarpit timeouts from the
set-timeout action. This is especially useful when using set-dst to
dynamically connect to servers.

This patch also adds the relevant fe_/be_/cur_ sample fetches for these
timeouts.

doc/configuration.txt
include/haproxy/action-t.h
include/haproxy/stream-t.h
src/action.c
src/backend.c
src/frontend.c
src/http_ana.c
src/stream.c

index 3cb38a50be97f3f0ff4b4d03afa6a03b2d86838f..b269fe969501d97cc1a4cc466ca772ac2b91d71b 100644 (file)
@@ -16357,20 +16357,24 @@ set-status <status> [reason <str>]
     http-response set-status 503 reason "Slow Down".
 
 
-set-timeout { client | server | tunnel } { <timeout> | <expr> }
+set-timeout { client | connect | queue | server | tarpit | tunnel } { <timeout> | <expr> }
   Usable in:  QUIC Ini|    TCP RqCon| RqSes| RqCnt| RsCnt|    HTTP Req| Res| Aft
                     - |          -  |   -  |   -  |   -  |          X |  X |  -
 
-  This action overrides the specified "client", "server" or "tunnel" timeout
-  for the current stream only. 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.
-
-  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. Likewise, client timeout is only relevant for frontend side.
-  Also the timeout value must be non-null to obtain the expected results.
+  This action overrides the specified "client", "connect", "queue", "server",
+  "tarpit" or "tunnel" timeout for the current stream only. Changing one timeout
+  does not influence any other timeouts, even if they are inherited from each
+  other during configuration parsing (see last example). 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.
+
+  Note that the connect, queue, server and tunnel timeouts are only relevant on
+  the backend side and thus this rule is only available for the proxies with
+  backend capabilities. Likewise, client timeout is only relevant for frontend
+  side. Tarpit timeout is available to both sides. The timeout value must be
+  non-null to obtain the expected results.
 
   Example:
     http-request set-timeout tunnel 5s
@@ -16380,6 +16384,18 @@ set-timeout { client | server | tunnel } { <timeout> | <expr> }
     http-response set-timeout tunnel 5s
     http-response set-timeout server res.hdr(X-Refresh-Seconds),mul(1000)
 
+  Example:
+    defaults
+      # This will set both tarpit and queue timeout to 5s as they are not
+      # defined
+      timeout connect 5s
+      timeout client 30s
+      timeout server 30s
+
+    listen foo
+      # This will only change the connect timeout to 10s without affecting
+      # queue or tarpit timeouts
+      http-request set-timeout connect 10s
 
 set-tos <tos> (deprecated)
   This is an alias for "set-fc-tos" (which should be used instead).
@@ -23828,12 +23844,18 @@ bc_src_port                                        integer
 bc_srv_queue                                       integer
 be_id                                              integer
 be_name                                            string
+be_connect_timeout                                 integer
+be_queue_timeout                                   integer
 be_server_timeout                                  integer
+be_tarpit_timeout                                  integer
 be_tunnel_timeout                                  integer
 bytes_in                                           integer
 bytes_out                                          integer
+cur_connect_timeout                                integer
 cur_client_timeout                                 integer
+cur_queue_timeout                                  integer
 cur_server_timeout                                 integer
+cur_tarpit_timeout                                 integer
 cur_tunnel_timeout                                 integer
 dst                                                ip
 dst_conn                                           integer
@@ -23867,6 +23889,7 @@ fc_src                                             ip
 fc_src_is_local                                    boolean
 fc_src_port                                        integer
 fc_unacked                                         integer
+fe_tarpit_timeout                                  integer
 fe_client_timeout                                  integer
 fe_defbe                                           string
 fe_id                                              integer
@@ -24161,6 +24184,11 @@ be_id : integer
   used in a frontend and no backend was used, it returns the current
   frontend's id. It can also be used in a tcp-check or an http-check ruleset.
 
+be_connect_timeout : integer
+  Returns the configuration value in millisecond for the connect timeout of the
+  current backend. This timeout can be overwritten by a "set-timeout" rule. See
+  also the "cur_connect_timeout".
+
 be_name : string
   Returns a string containing the current backend's name. It can be used in
   frontends with responses to check which backend processed the request. If
@@ -24168,11 +24196,21 @@ be_name : string
   frontend's name. It can also be used in a tcp-check or an http-check
   ruleset.
 
+be_queue_timeout : integer
+  Returns the configuration value in millisecond for the queue timeout of the
+  current backend. This timeout can be overwritten by a "set-timeout" rule. See
+  also the "cur_queue_timeout".
+
 be_server_timeout : integer
   Returns the configuration value in millisecond for the server timeout of the
   current backend. This timeout can be overwritten by a "set-timeout" rule. See
   also the "cur_server_timeout".
 
+be_tarpit_timeout : integer
+  Returns the configuration value in millisecond for the queue timeout of the
+  current backend. This timeout can be overwritten by a "set-timeout" rule. See
+  also the "cur_tarpit_timeout".
+
 be_tunnel_timeout : integer
   Returns the configuration value in millisecond for the tunnel timeout of the
   current backend. This timeout can be overwritten by a "set-timeout" rule. See
@@ -24184,16 +24222,32 @@ bytes_in : integer
 bytes_out : integer
   See "res.bytes_in".
 
+cur_connect_timeout : integer
+  Returns the currently applied connect timeout in millisecond for the stream.
+  In the default case, this will be equal to be_connect_timeout unless a
+  "set-timeout" rule has been applied. See also "be_connect_timeout".
+
 cur_client_timeout : integer
   Returns the currently applied client timeout in millisecond for the stream.
   In the default case, this will be equal to fe_client_timeout unless a
   "set-timeout" rule has been applied. See also "fe_client_timeout".
 
+cur_queue_timeout : integer
+  Returns the currently applied queue timeout in millisecond for the stream.
+  In the default case, this will be equal to be_queue_timeout unless a
+  "set-timeout" rule has been applied. See also "be_queue_timeout".
+
 cur_server_timeout : integer
   Returns the currently applied server timeout in millisecond for the stream.
   In the default case, this will be equal to be_server_timeout unless a
   "set-timeout" rule has been applied. See also "be_server_timeout".
 
+cur_tarpit_timeout : integer
+  Returns the currently applied tarpit timeout in millisecond for the stream.
+  In the default case, this will be equal to fe_tarpit_timeout/be_tarpit_timeout
+  unless a "set-timeout" rule has been applied. See also "fe_tarpit_timeout"
+  and "be_tarpit_timeout".
+
 cur_tunnel_timeout : integer
   Returns the currently applied tunnel timeout in millisecond for the stream.
   In the default case, this will be equal to be_tunnel_timeout unless a
@@ -24578,6 +24632,10 @@ fe_name : string
   backends to check from which frontend it was called, or to stick all users
   coming via a same frontend to the same server.
 
+fe_tarpit_timeout : integer
+  Returns the configuration value in millisecond for the tarpit timeout of the
+  current frontend. This timeout can be overwritten by a "set-timeout" rule.
+
 req.bytes_in : integer
   This returns the number of bytes received from the client. The value
   corresponds to what was received by HAProxy, including some headers and some
index b2693ca00df8a27dfeeec12741a79f77364cfc58..96b0243ae95caeaa090e931f129f82c1d0aba9d6 100644 (file)
@@ -102,7 +102,10 @@ enum act_name {
 
 /* Timeout name valid for a set-timeout rule */
 enum act_timeout_name {
+       ACT_TIMEOUT_CONNECT,
        ACT_TIMEOUT_SERVER,
+       ACT_TIMEOUT_QUEUE,
+       ACT_TIMEOUT_TARPIT,
        ACT_TIMEOUT_TUNNEL,
        ACT_TIMEOUT_CLIENT,
 };
index 77d8112bc03b1e79ad859d568eb0f00df7ff7aa8..b4677cb2f374235bacd2dc044277a884f971000e 100644 (file)
@@ -320,7 +320,10 @@ struct stream {
        struct list *current_rule_list;         /* this is used to store the current executed rule list. */
        void *current_rule;                     /* this is used to store the current rule to be resumed. */
        int rules_exp;                          /* expiration date for current rules execution */
-       int tunnel_timeout;
+       int tunnel_timeout;                     /* per-stream tunnel timeout, set by set-timeout action */
+       int connect_timeout;                    /* per-stream connect timeout, set by set-timeout action */
+       int queue_timeout;                      /* per-stream queue timeout, set by set-timeout action */
+       int tarpit_timeout;                     /* per-stream tarpit timeout, set by set-timeout action */
 
        struct {
                void *ptr;                      /* Pointer on the entity  (def: NULL) */
index 47f5f8607a1e85e9f865c24ff813ee25dd839a0e..8a1486d10c5694335d02c537815170e4867d14ee 100644 (file)
@@ -188,13 +188,30 @@ int cfg_parse_rule_set_timeout(const char **args, int idx, struct act_rule *rule
        const char *res;
        const char *timeout_name = args[idx++];
 
-       if (strcmp(timeout_name, "server") == 0) {
+       if (strcmp(timeout_name, "connect") == 0) {
+               if (!(px->cap & PR_CAP_BE)) {
+                       memprintf(err, "'%s' has no backend capability", px->id);
+                       return -1;
+               }
+               rule->arg.timeout.type = ACT_TIMEOUT_CONNECT;
+       }
+       else if (strcmp(timeout_name, "server") == 0) {
                if (!(px->cap & PR_CAP_BE)) {
                        memprintf(err, "'%s' has no backend capability", px->id);
                        return -1;
                }
                rule->arg.timeout.type = ACT_TIMEOUT_SERVER;
        }
+       else if (strcmp(timeout_name, "queue") == 0) {
+               if (!(px->cap & PR_CAP_BE)) {
+                       memprintf(err, "'%s' has no backend capability", px->id);
+                       return -1;
+               }
+               rule->arg.timeout.type = ACT_TIMEOUT_QUEUE;
+       }
+       else if (strcmp(timeout_name, "tarpit") == 0) {
+               rule->arg.timeout.type = ACT_TIMEOUT_TARPIT;
+       }
        else if (strcmp(timeout_name, "tunnel") == 0) {
                if (!(px->cap & PR_CAP_BE)) {
                        memprintf(err, "'%s' has no backend capability", px->id);
@@ -211,7 +228,7 @@ int cfg_parse_rule_set_timeout(const char **args, int idx, struct act_rule *rule
        }
        else {
                memprintf(err,
-                         "'set-timeout' rule supports 'server'/'tunnel'/'client' (got '%s')",
+                         "'set-timeout' rule supports 'client'/'connect'/'queue'/'server'/'tarpit'/'tunnel' (got '%s')",
                          timeout_name);
                return -1;
        }
index abe3942d340fa2aea4a33eb918ad3c7617e14500..49fe787ea9554c8345099b31547a56d7c5567f51 100644 (file)
@@ -2254,7 +2254,7 @@ int connect_server(struct stream *s)
 #endif
 
        /* set connect timeout */
-       s->conn_exp = tick_add_ifset(now_ms, s->be->timeout.connect);
+       s->conn_exp = tick_add_ifset(now_ms, s->connect_timeout);
 
        if (srv) {
                int count;
@@ -2377,7 +2377,7 @@ int srv_redispatch_connect(struct stream *s)
                return 1;
 
        case SRV_STATUS_QUEUED:
-               s->conn_exp = tick_add_ifset(now_ms, s->be->timeout.queue);
+               s->conn_exp = tick_add_ifset(now_ms, s->queue_timeout);
                s->scb->state = SC_ST_QUE;
 
                /* handle the unlikely event where we added to the server's
@@ -3741,6 +3741,42 @@ smp_fetch_srv_uweight(const struct arg *args, struct sample *smp, const char *kw
        return 1;
 }
 
+static int
+smp_fetch_be_connect_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
+{
+       struct proxy *px = NULL;
+
+       if (smp->strm)
+               px = smp->strm->be;
+       else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
+               px = __objt_check(smp->sess->origin)->proxy;
+       if (!px)
+               return 0;
+
+       smp->flags = SMP_F_VOL_TXN;
+       smp->data.type = SMP_T_SINT;
+       smp->data.u.sint = TICKS_TO_MS(px->timeout.connect);
+       return 1;
+}
+
+static int
+smp_fetch_be_queue_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
+{
+       struct proxy *px = NULL;
+
+       if (smp->strm)
+               px = smp->strm->be;
+       else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
+               px = __objt_check(smp->sess->origin)->proxy;
+       if (!px)
+               return 0;
+
+       smp->flags = SMP_F_VOL_TXN;
+       smp->data.type = SMP_T_SINT;
+       smp->data.u.sint = TICKS_TO_MS(px->timeout.queue);
+       return 1;
+}
+
 static int
 smp_fetch_be_server_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
 {
@@ -3759,6 +3795,24 @@ smp_fetch_be_server_timeout(const struct arg *args, struct sample *smp, const ch
        return 1;
 }
 
+static int
+smp_fetch_be_tarpit_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
+{
+       struct proxy *px = NULL;
+
+       if (smp->strm)
+               px = smp->strm->be;
+       else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK)
+               px = __objt_check(smp->sess->origin)->proxy;
+       if (!px)
+               return 0;
+
+       smp->flags = SMP_F_VOL_TXN;
+       smp->data.type = SMP_T_SINT;
+       smp->data.u.sint = TICKS_TO_MS(px->timeout.tarpit);
+       return 1;
+}
+
 static int
 smp_fetch_be_tunnel_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
 {
@@ -3859,8 +3913,11 @@ static struct sample_fetch_kw_list smp_kws = {ILH, {
        { "be_conn_free",      smp_fetch_be_conn_free,      ARG1(1,BE),  NULL, SMP_T_SINT, SMP_USE_INTRN, },
        { "be_id",             smp_fetch_be_id,             0,           NULL, SMP_T_SINT, SMP_USE_BKEND, },
        { "be_name",           smp_fetch_be_name,           0,           NULL, SMP_T_STR,  SMP_USE_BKEND, },
+       { "be_connect_timeout",smp_fetch_be_connect_timeout,0,           NULL, SMP_T_SINT, SMP_USE_BKEND, },
+       { "be_queue_timeout",  smp_fetch_be_queue_timeout,  0,           NULL, SMP_T_SINT, SMP_USE_BKEND, },
        { "be_server_timeout", smp_fetch_be_server_timeout, 0,           NULL, SMP_T_SINT, SMP_USE_BKEND, },
        { "be_sess_rate",      smp_fetch_be_sess_rate,      ARG1(1,BE),  NULL, SMP_T_SINT, SMP_USE_INTRN, },
+       { "be_tarpit_timeout", smp_fetch_be_tarpit_timeout, 0,           NULL, SMP_T_SINT, SMP_USE_BKEND, },
        { "be_tunnel_timeout", smp_fetch_be_tunnel_timeout, 0,           NULL, SMP_T_SINT, SMP_USE_BKEND, },
        { "connslots",         smp_fetch_connslots,         ARG1(1,BE),  NULL, SMP_T_SINT, SMP_USE_INTRN, },
        { "nbsrv",             smp_fetch_nbsrv,             ARG1(1,BE),  NULL, SMP_T_SINT, SMP_USE_INTRN, },
index aab13e4466410766fe719fdca6ef1a28d9dac7a6..1df11fcb7c14aa8d8e30aec3014190572fd0ba0f 100644 (file)
@@ -314,12 +314,22 @@ smp_fetch_fe_client_timeout(const struct arg *args, struct sample *smp, const ch
        return 1;
 }
 
+static int
+smp_fetch_fe_tarpit_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
+{
+       smp->flags = SMP_F_VOL_TXN;
+       smp->data.type = SMP_T_SINT;
+       smp->data.u.sint = TICKS_TO_MS(smp->sess->fe->timeout.tarpit);
+       return 1;
+}
+
 
 /* Note: must not be declared <const> as its list will be overwritten.
  * Please take care of keeping this list alphabetically sorted.
  */
 static struct sample_fetch_kw_list smp_kws = {ILH, {
        { "fe_client_timeout", smp_fetch_fe_client_timeout, 0,          NULL, SMP_T_SINT, SMP_USE_FTEND, },
+       { "fe_tarpit_timeout", smp_fetch_fe_tarpit_timeout, 0,          NULL, SMP_T_SINT, SMP_USE_FTEND, },
        { "fe_conn",           smp_fetch_fe_conn,           ARG1(1,FE), NULL, SMP_T_SINT, SMP_USE_INTRN, },
        { "fe_defbe",          smp_fetch_fe_defbe,          0,          NULL, SMP_T_STR,  SMP_USE_FTEND, },
        { "fe_id",             smp_fetch_fe_id,             0,          NULL, SMP_T_SINT, SMP_USE_FTEND, },
index 937f580b4c573d3793965ed8f27b38708c66281f..f004e41d94776a9548ed23887781cf4981d15b32 100644 (file)
@@ -564,7 +564,7 @@ int http_process_req_common(struct stream *s, struct channel *req, int an_bit, s
 
        req->analysers &= AN_REQ_FLT_END; /* remove switching rules etc... */
        req->analysers |= AN_REQ_HTTP_TARPIT;
-       req->analyse_exp = tick_add_ifset(now_ms,  s->be->timeout.tarpit);
+       req->analyse_exp = tick_add_ifset(now_ms, s->tarpit_timeout ? s->tarpit_timeout : s->be->timeout.tarpit);
        if (!req->analyse_exp)
                req->analyse_exp = tick_add(now_ms, 0);
        stream_inc_http_err_ctr(s);
index d760c8cba8f4e6c54a742ff51b007864e60af9b1..63cbade0a4ee0e86c0e64670d76359e6d4dfe0c8 100644 (file)
@@ -555,6 +555,9 @@ struct stream *stream_new(struct session *sess, struct stconn *sc, struct buffer
        s->resolv_ctx.hostname_dn_len = 0;
        s->resolv_ctx.parent = NULL;
 
+       s->connect_timeout = TICK_ETERNITY;
+       s->queue_timeout = TICK_ETERNITY;
+       s->tarpit_timeout = TICK_ETERNITY;
        s->tunnel_timeout = TICK_ETERNITY;
 
        LIST_APPEND(&th_ctx->streams, &s->list);
@@ -949,10 +952,22 @@ int stream_set_timeout(struct stream *s, enum act_timeout_name name, int timeout
                s->scf->ioto = timeout;
                return 1;
 
+       case ACT_TIMEOUT_CONNECT:
+               s->connect_timeout = timeout;
+               return 1;
+
+       case ACT_TIMEOUT_QUEUE:
+               s->queue_timeout = timeout;
+               return 1;
+
        case ACT_TIMEOUT_SERVER:
                s->scb->ioto = timeout;
                return 1;
 
+       case ACT_TIMEOUT_TARPIT:
+               s->tarpit_timeout = timeout;
+               return 1;
+
        case ACT_TIMEOUT_TUNNEL:
                s->tunnel_timeout = timeout;
                return 1;
@@ -1248,6 +1263,10 @@ static int process_switching_rules(struct stream *s, struct channel *req, int an
        /* Se the max connection retries for the stream. may be overwritten later */
        s->max_retries = s->be->conn_retries;
 
+       /* Set the queue and connect timeouts. May be overwritten later */
+       s->connect_timeout = s->be->timeout.connect;
+       s->queue_timeout = s->be->timeout.queue;
+
        /* we don't want to run the TCP or HTTP filters again if the backend has not changed */
        if (fe == s->be) {
                s->req.analysers &= ~AN_REQ_INSPECT_BE;
@@ -4383,6 +4402,17 @@ static struct action_kw_list stream_http_after_res_actions =  { ILH, {
 
 INITCALL1(STG_REGISTER, http_after_res_keywords_register, &stream_http_after_res_actions);
 
+static int smp_fetch_cur_connect_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
+{
+       smp->flags = SMP_F_VOL_TXN;
+       smp->data.type = SMP_T_SINT;
+       if (!smp->strm)
+               return 0;
+
+       smp->data.u.sint = TICKS_TO_MS(smp->strm->connect_timeout);
+       return 1;
+}
+
 static int smp_fetch_cur_client_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
 {
        smp->flags = SMP_F_VOL_TXN;
@@ -4405,6 +4435,34 @@ static int smp_fetch_cur_server_timeout(const struct arg *args, struct sample *s
        return 1;
 }
 
+static int smp_fetch_cur_queue_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
+{
+       smp->flags = SMP_F_VOL_TXN;
+       smp->data.type = SMP_T_SINT;
+       if (!smp->strm)
+               return 0;
+
+       smp->data.u.sint = TICKS_TO_MS(smp->strm->queue_timeout);
+       return 1;
+}
+
+static int smp_fetch_cur_tarpit_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
+{
+       smp->flags = SMP_F_VOL_TXN;
+       smp->data.type = SMP_T_SINT;
+       if (!smp->strm)
+               return 0;
+
+       if (smp->strm->tarpit_timeout)
+               smp->data.u.sint = TICKS_TO_MS(smp->strm->tarpit_timeout);
+       else if (smp->strm->be)
+               smp->data.u.sint = TICKS_TO_MS(smp->strm->be->timeout.tarpit);
+       else
+               smp->data.u.sint = TICKS_TO_MS(smp->sess->fe->timeout.tarpit);
+
+       return 1;
+}
+
 static int smp_fetch_cur_tunnel_timeout(const struct arg *args, struct sample *smp, const char *km, void *private)
 {
        smp->flags = SMP_F_VOL_TXN;
@@ -4609,8 +4667,11 @@ static int smp_fetch_redispatched(const struct arg *args, struct sample *smp, co
  * Please take care of keeping this list alphabetically sorted.
  */
 static struct sample_fetch_kw_list smp_kws = {ILH, {
+       { "cur_connect_timeout",smp_fetch_cur_connect_timeout,0, NULL, SMP_T_SINT, SMP_USE_BKEND, },
        { "cur_client_timeout", smp_fetch_cur_client_timeout, 0, NULL, SMP_T_SINT, SMP_USE_FTEND, },
        { "cur_server_timeout", smp_fetch_cur_server_timeout, 0, NULL, SMP_T_SINT, SMP_USE_BKEND, },
+       { "cur_queue_timeout",  smp_fetch_cur_queue_timeout,  0, NULL, SMP_T_SINT, SMP_USE_BKEND, },
+       { "cur_tarpit_timeout", smp_fetch_cur_tarpit_timeout, 0, NULL, SMP_T_SINT, SMP_USE_FTEND | SMP_USE_BKEND, },
        { "cur_tunnel_timeout", smp_fetch_cur_tunnel_timeout, 0, NULL, SMP_T_SINT, SMP_USE_BKEND, },
        { "last_entity",        smp_fetch_last_entity,        0, NULL, SMP_T_STR,  SMP_USE_INTRN, },
        { "last_rule_file",     smp_fetch_last_rule_file,     0, NULL, SMP_T_STR,  SMP_USE_INTRN, },