From: Nenad Merdanovic Date: Wed, 18 Feb 2026 14:56:36 +0000 (+0100) Subject: MEDIUM: Add connect/queue/tarpit timeouts to set-timeout X-Git-Tag: v3.4-dev5~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5a079d1811786aa72bd1d5abf1311b0500c1f163;p=thirdparty%2Fhaproxy.git MEDIUM: Add connect/queue/tarpit timeouts to set-timeout 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. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 3cb38a50b..b269fe969 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -16357,20 +16357,24 @@ set-status [reason ] http-response set-status 503 reason "Slow Down". -set-timeout { client | server | tunnel } { | } +set-timeout { client | connect | queue | server | tarpit | tunnel } { | } 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 } { | } 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 (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 diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h index b2693ca00..96b0243ae 100644 --- a/include/haproxy/action-t.h +++ b/include/haproxy/action-t.h @@ -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, }; diff --git a/include/haproxy/stream-t.h b/include/haproxy/stream-t.h index 77d8112bc..b4677cb2f 100644 --- a/include/haproxy/stream-t.h +++ b/include/haproxy/stream-t.h @@ -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) */ diff --git a/src/action.c b/src/action.c index 47f5f8607..8a1486d10 100644 --- a/src/action.c +++ b/src/action.c @@ -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; } diff --git a/src/backend.c b/src/backend.c index abe3942d3..49fe787ea 100644 --- a/src/backend.c +++ b/src/backend.c @@ -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, }, diff --git a/src/frontend.c b/src/frontend.c index aab13e446..1df11fcb7 100644 --- a/src/frontend.c +++ b/src/frontend.c @@ -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 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, }, diff --git a/src/http_ana.c b/src/http_ana.c index 937f580b4..f004e41d9 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -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); diff --git a/src/stream.c b/src/stream.c index d760c8cba..63cbade0a 100644 --- a/src/stream.c +++ b/src/stream.c @@ -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, },