From: Christopher Faulet Date: Mon, 19 Oct 2026 13:41:43 +0000 (+0200) Subject: MEDIUM: filters: eval the backend instances first on the response path X-Git-Tag: v3.5-dev10~105 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=4ac7c48ed160e5b9a745e7a152066e9c5cf0c5ec;p=haproxy.git MEDIUM: filters: eval the backend instances first on the response path When the backend filters are attached to a stream, they are appended to the global and request filter lists after the frontend ones, which is the expected order: the request reaches the frontend filters first. But they were also appended to the response filter list, while a response travels in the opposite direction and reaches the backend filters first. In the new filter mode, the backend instances are now prepended to the response filter list, preserving their own response order, so a response is evaluated by the backend instances before the frontend ones. The request path is unchanged. The regression tests relying on the response evaluation order of the instance mode were updated accordingly. --- diff --git a/reg-tests/filters/lua-defaults.vtc b/reg-tests/filters/lua-defaults.vtc index 073df7d77..dca28667e 100644 --- a/reg-tests/filters/lua-defaults.vtc +++ b/reg-tests/filters/lua-defaults.vtc @@ -108,7 +108,7 @@ client c1 -connect ${h1_first_sock} { txreq -req POST -body "ping" rxresp expect resp.status == 200 - expect resp.http.x-probe-order == "F.base-b;F1.local-c;F1.local-a;BA.local-c;BA.local-d;" + expect resp.http.x-probe-order == "BA.local-c;BA.local-d;F.base-b;F1.local-c;F1.local-a;" expect resp.http.x-probe-errors == expect resp.body == "pong" } -run @@ -119,7 +119,7 @@ client c2 -connect ${h1_sibling_sock} { txreq rxresp expect resp.status == 200 - expect resp.http.x-probe-order == "F.base-a;B.base-b;B.base-c;" + expect resp.http.x-probe-order == "B.base-b;B.base-c;F.base-a;" expect resp.http.x-probe-errors == expect resp.body == "sibling" } -run diff --git a/reg-tests/filters/lua-keepalive.vtc b/reg-tests/filters/lua-keepalive.vtc index 44cf44364..7c6c99e4f 100644 --- a/reg-tests/filters/lua-keepalive.vtc +++ b/reg-tests/filters/lua-keepalive.vtc @@ -97,9 +97,9 @@ client c1 -connect ${h1_fe_sock} { txreq -req POST -url /instances -body "first" rxresp expect resp.status == 200 - expect resp.http.x-probe-order == "F.b;F.a;D.c;D.a;" - expect resp.http.x-probe-instances == "F.b:1;F.a:2;D.c:1;D.a:2;" - expect resp.http.x-probe-request-bytes == "F.a:5;D.c:5;" + expect resp.http.x-probe-order == "D.c;D.a;F.b;F.a;" + expect resp.http.x-probe-instances == "D.c:1;D.a:2;F.b:1;F.a:2;" + expect resp.http.x-probe-request-bytes == "D.c:5;F.a:5;" expect resp.http.x-probe-errors == expect resp.body == "first response" @@ -127,9 +127,9 @@ client c1 -connect ${h1_fe_sock} { txreq -req POST -url /instances -body "fourth" rxresp expect resp.status == 200 - expect resp.http.x-probe-order == "F.b;F.a;D.c;D.a;" - expect resp.http.x-probe-instances == "F.b:7;F.a:8;D.c:3;D.a:4;" - expect resp.http.x-probe-request-bytes == "F.a:6;D.c:6;" + expect resp.http.x-probe-order == "D.c;D.a;F.b;F.a;" + expect resp.http.x-probe-instances == "D.c:3;D.a:4;F.b:7;F.a:8;" + expect resp.http.x-probe-request-bytes == "D.c:6;F.a:6;" expect resp.http.x-probe-errors == expect resp.body == "fourth response" } -run @@ -138,8 +138,8 @@ client c2 -connect ${h1_fe_sock} { txreq -req POST -url /instances -body "new" rxresp expect resp.status == 200 - expect resp.http.x-probe-instances == "F.b:9;F.a:10;D.c:5;D.a:6;" - expect resp.http.x-probe-request-bytes == "F.a:3;D.c:3;" + expect resp.http.x-probe-instances == "D.c:5;D.a:6;F.b:9;F.a:10;" + expect resp.http.x-probe-request-bytes == "D.c:3;F.a:3;" expect resp.http.x-probe-errors == expect resp.body == "new connection" } -run diff --git a/reg-tests/filters/lua-per-thread.vtc b/reg-tests/filters/lua-per-thread.vtc index 0404e3713..866206612 100644 --- a/reg-tests/filters/lua-per-thread.vtc +++ b/reg-tests/filters/lua-per-thread.vtc @@ -72,9 +72,9 @@ client c1 -connect ${h1_one_sock} { txreq rxresp expect resp.status == 200 - expect resp.http.x-probe-order == "F.b;F.a;B.c;B.a;" - expect resp.http.x-probe-threads == "F.b:1;F.a:1;B.c:1;B.a:1;" - expect resp.http.x-probe-instances == "F.b:1;F.a:2;B.c:2;B.a:1;" + expect resp.http.x-probe-order == "B.c;B.a;F.b;F.a;" + expect resp.http.x-probe-threads == "B.c:1;B.a:1;F.b:1;F.a:1;" + expect resp.http.x-probe-instances == "B.c:2;B.a:1;F.b:1;F.a:2;" expect resp.http.x-probe-errors == expect resp.body == "thread one" } -run @@ -83,9 +83,9 @@ client c2 -connect ${h1_two_sock} { txreq rxresp expect resp.status == 200 - expect resp.http.x-probe-order == "F.b;F.a;B.c;B.a;" - expect resp.http.x-probe-threads == "F.b:2;F.a:2;B.c:2;B.a:2;" - expect resp.http.x-probe-instances == "F.b:1;F.a:2;B.c:2;B.a:1;" + expect resp.http.x-probe-order == "B.c;B.a;F.b;F.a;" + expect resp.http.x-probe-threads == "B.c:2;B.a:2;F.b:2;F.a:2;" + expect resp.http.x-probe-instances == "B.c:2;B.a:1;F.b:1;F.a:2;" expect resp.http.x-probe-errors == expect resp.body == "thread two" } -run @@ -94,8 +94,8 @@ client c3 -connect ${h1_one_sock} { txreq rxresp expect resp.status == 200 - expect resp.http.x-probe-threads == "F.b:1;F.a:1;B.c:1;B.a:1;" - expect resp.http.x-probe-instances == "F.b:3;F.a:4;B.c:4;B.a:3;" + expect resp.http.x-probe-threads == "B.c:1;B.a:1;F.b:1;F.a:1;" + expect resp.http.x-probe-instances == "B.c:4;B.a:3;F.b:3;F.a:4;" expect resp.http.x-probe-errors == expect resp.body == "thread one again" } -run @@ -104,8 +104,8 @@ client c4 -connect ${h1_two_sock} { txreq rxresp expect resp.status == 200 - expect resp.http.x-probe-threads == "F.b:2;F.a:2;B.c:2;B.a:2;" - expect resp.http.x-probe-instances == "F.b:3;F.a:4;B.c:4;B.a:3;" + expect resp.http.x-probe-threads == "B.c:2;B.a:2;F.b:2;F.a:2;" + expect resp.http.x-probe-instances == "B.c:4;B.a:3;F.b:3;F.a:4;" expect resp.http.x-probe-errors == expect resp.body == "thread two again" } -run diff --git a/reg-tests/filters/lua-resume.vtc b/reg-tests/filters/lua-resume.vtc index e99a6ac2f..3781be318 100644 --- a/reg-tests/filters/lua-resume.vtc +++ b/reg-tests/filters/lua-resume.vtc @@ -64,9 +64,9 @@ client c1 -connect ${h1_fe_sock} { txreq -req POST -body "abcdefgh" rxresp expect resp.status == 200 - expect resp.http.x-probe-order == "F.c;F.a;F.b;B.a;B.c;B.b;" - expect resp.http.x-probe-instances == "F.c:2;F.a:1;F.b:3;B.a:2;B.c:1;B.b:3;" - expect resp.http.x-probe-request-bytes == "F.b:8;B.a:8;B.c:8;" + expect resp.http.x-probe-order == "B.a;B.c;B.b;F.c;F.a;F.b;" + expect resp.http.x-probe-instances == "B.a:2;B.c:1;B.b:3;F.c:2;F.a:1;F.b:3;" + expect resp.http.x-probe-request-bytes == "B.a:8;B.c:8;F.b:8;" expect resp.http.x-probe-errors == expect resp.body == "response after waits" @@ -76,9 +76,9 @@ client c1 -connect ${h1_fe_sock} { chunked "" rxresp expect resp.status == 200 - expect resp.http.x-probe-order == "F.c;F.a;F.b;B.a;B.c;B.b;" - expect resp.http.x-probe-instances == "F.c:5;F.a:4;F.b:6;B.a:5;B.c:4;B.b:6;" - expect resp.http.x-probe-request-bytes == "F.b:8;B.a:8;B.c:8;" + expect resp.http.x-probe-order == "B.a;B.c;B.b;F.c;F.a;F.b;" + expect resp.http.x-probe-instances == "B.a:5;B.c:4;B.b:6;F.c:5;F.a:4;F.b:6;" + expect resp.http.x-probe-request-bytes == "B.a:8;B.c:8;F.b:8;" expect resp.http.x-probe-errors == expect resp.body == "another response" barrier b_drained sync diff --git a/reg-tests/filters/lua-retry.vtc b/reg-tests/filters/lua-retry.vtc index afc5b6f83..56a661030 100644 --- a/reg-tests/filters/lua-retry.vtc +++ b/reg-tests/filters/lua-retry.vtc @@ -66,8 +66,8 @@ client c1 -connect ${h1_fe_sock} { expect resp.status == 200 expect resp.http.x-retries == "1" expect resp.http.x-redispatched == "1" - expect resp.http.x-probe-order == "F.b;F.a;B.a;B.c;" - expect resp.http.x-probe-instances == "F.b:1;F.a:2;B.a:2;B.c:1;" + expect resp.http.x-probe-order == "B.a;B.c;F.b;F.a;" + expect resp.http.x-probe-instances == "B.a:2;B.c:1;F.b:1;F.a:2;" expect resp.http.x-probe-errors == expect resp.body == "successful retry" barrier b_drained sync diff --git a/reg-tests/filters/stream-order.vtc b/reg-tests/filters/stream-order.vtc index f2f168470..1d3108579 100644 --- a/reg-tests/filters/stream-order.vtc +++ b/reg-tests/filters/stream-order.vtc @@ -58,7 +58,7 @@ client c1 -connect ${h1_fe_sock} { txreq -req POST -body "ping" rxresp expect resp.status == 200 - expect resp.http.x-filter-order == "Fb:3;Fc:1;Fa:2;Ba:2;Bb:1;Bc:3;" + expect resp.http.x-filter-order == "Ba:2;Bb:1;Bc:3;Fb:3;Fc:1;Fa:2;" expect resp.http.x-filter-start == expect resp.body == "pong" } -run @@ -100,7 +100,7 @@ client c2 -connect ${h2_fe_sock} { txreq rxresp expect resp.status == 200 - expect resp.http.x-filter-order == "Fa:1;Fb:2;Ba:1;Bb:2;" + expect resp.http.x-filter-order == "Ba:1;Bb:2;Fa:1;Fb:2;" expect resp.http.x-filter-start == } -run diff --git a/src/filters.c b/src/filters.c index cd3a82817..00a6c1c3f 100644 --- a/src/filters.c +++ b/src/filters.c @@ -1028,6 +1028,11 @@ flt_stream_add_filter(struct stream *s, struct flt_conf *fconf, unsigned int fla * configuration pointers to build the channel lists; disabled, incompatible * or declined filters have no stream filter instance to link. Never store * per-stream pointers in shared filter instances. + * + * The request list is always filled in the proxy's request order. The + * response list is filled in the proxy's response order for the frontend + * filters, but the backend filters are prepended, so a response reaches the + * backend filters before the frontend ones. */ static int flt_stream_add_proxy_filters(struct stream *s, struct proxy *px, unsigned int flags) { @@ -1080,13 +1085,27 @@ static int flt_stream_add_proxy_filters(struct stream *s, struct proxy *px, unsi } } } - list_for_each_entry(inst, &px->filter_res_instances, res.list) { - if (!inst->enabled || !inst->fconf) - continue; - list_for_each_entry(filter, &strm_flt(s)->filters, list) { - if (filter->config == inst->fconf) { - LIST_APPEND(&s->res.flt.filters, &filter->res_list); - break; + if (flags & FLT_FL_IS_BACKEND_FILTER) { + list_for_each_entry_rev(inst, &px->filter_res_instances, res.list) { + if (!inst->enabled || !inst->fconf) + continue; + list_for_each_entry(filter, &strm_flt(s)->filters, list) { + if (filter->config == inst->fconf) { + LIST_INSERT(&s->res.flt.filters, &filter->res_list); + break; + } + } + } + } + else { + list_for_each_entry(inst, &px->filter_res_instances, res.list) { + if (!inst->enabled || !inst->fconf) + continue; + list_for_each_entry(filter, &strm_flt(s)->filters, list) { + if (filter->config == inst->fconf) { + LIST_APPEND(&s->res.flt.filters, &filter->res_list); + break; + } } } }