From: Christopher Faulet Date: Fri, 2 Oct 2026 16:06:33 +0000 (+0200) Subject: REGTESTS: filters: Add a test for per-proxy stream filter ordering X-Git-Tag: v3.5-dev9~64 X-Git-Url: https://git.kaiwu.me/$%7BGITURL%7D$%7Bcid%7D?a=commitdiff_plain;h=0b058112eb71016c6ebee78b1b78759ca09ced73;p=haproxy.git REGTESTS: filters: Add a test for per-proxy stream filter ordering Add a Lua filter exposing the attach order with a per-proxy ordinal and the request/response evaluation order in a header. The reg-test checks that instance-mode filters are attached in the global order and linked to the request and response channels in their own order, that disabled, declined and non-HTX filters are skipped, and that the legacy or instance model is selected independently for each proxy, including mix-and-match frontends and backends, and a listen proxy. --- diff --git a/reg-tests/filters/stream-order.lua b/reg-tests/filters/stream-order.lua new file mode 100644 index 000000000..77d3bf5d1 --- /dev/null +++ b/reg-tests/filters/stream-order.lua @@ -0,0 +1,49 @@ +-- Attach ordinals expose the global instance order; headers expose each +-- channel order. Counters are scoped to a configured proxy, not shared streams. +local counters = {} + +local function register(name, htx, decline) + local F = { + id = "stream-order-" .. name, + flags = htx and filter.FLT_CFG_FL_HTX or 0, + tag = name, + } + + function F:new() + if decline then + return nil + end + counters[self.scope] = (counters[self.scope] or 0) + 1 + return setmetatable({ + ordinal = counters[self.scope], + started = {}, + }, { __index = self }) + end + + function F:start_analyze(txn, chn) + self.started[chn:is_resp() and 2 or 1] = true + return filter.CONTINUE + end + + function F:http_headers(txn, msg) + if not self.started[msg.channel:is_resp() and 2 or 1] then + msg:set_header("x-filter-start", "missing") + end + local previous = msg:get_headers()["x-filter-order"] + local order = previous and previous[0] or "" + msg:set_header("x-filter-order", order .. self.scope .. self.tag .. ":" .. self.ordinal .. ";") + return filter.CONTINUE + end + + core.register_filter(name, F, function(conf, args) + conf.scope = args[1] + return conf + end) +end + +register("a", true, false) +register("b", true, false) +register("c", true, false) +register("disabled", true, false) +register("skip", true, true) +register("tcp-only", false, false) diff --git a/reg-tests/filters/stream-order.vtc b/reg-tests/filters/stream-order.vtc new file mode 100644 index 000000000..f2f168470 --- /dev/null +++ b/reg-tests/filters/stream-order.vtc @@ -0,0 +1,184 @@ +varnishtest "Select legacy or instance filter instance lists per proxy" + +feature cmd "$HAPROXY_PROGRAM -cc 'feature(LUA)'" +feature ignore_unknown_macro + +# Ordinals are assigned by the attach callback, so these headers check global +# construction order as well as the independent request/response list order. +server s1 { + rxreq + expect req.http.x-filter-order == "Fb:3;Fa:2;Fc:1;Bc:3;Ba:2;Bb:1;" + expect req.http.x-filter-start == + expect req.body == "ping" + txresp -body "pong" +} -start + +haproxy h1 -conf { + global + tune.lua.bool-sample-conversion normal + nbthread 1 + lua-load "${testdir}/stream-order.lua" + + defaults + mode http + timeout connect "${HAPROXY_TEST_TIMEOUT-5s}" + timeout client "${HAPROXY_TEST_TIMEOUT-5s}" + timeout server "${HAPROXY_TEST_TIMEOUT-5s}" + + frontend fe + bind "fd@${fe}" + filter-config lua id a enabled F + filter-config lua id b enabled F + filter-config lua id c enabled F + filter-config lua id disabled F + filter-config lua id skip enabled F + filter-config lua id tcp-only enabled F + # Exercise unused channel links when a single-side instance is freed. + filter-config bwlim-in id upload enabled default-limit 1m default-period 1s + filter-config bwlim-out id download enabled default-limit 1m default-period 1s + filter-sequence global lua/c:before(lua/a) + filter-sequence request lua/b:before(lua/a) + filter-sequence response lua/a:after(lua/c) + default_backend be + + backend be + filter-config lua id a enabled B + filter-config lua id b enabled B + filter-config lua id c enabled B + filter-config lua id disabled B + filter-config lua id skip enabled B + filter-config lua id tcp-only enabled B + filter-sequence global lua/b:before(lua/a) + filter-sequence request lua/c:before(lua/a) + filter-sequence response lua/c:after(lua/b) + server s1 ${s1_addr}:${s1_port} +} -start + +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-start == + expect resp.body == "pong" +} -run + +# The choice is per proxy: a legacy frontend can use a instance-mode backend. +server s2 { + rxreq + expect req.http.x-filter-order == "Fa:1;Fb:2;Bb:2;Ba:1;" + expect req.http.x-filter-start == + txresp +} -start + +haproxy h2 -conf { + global + tune.lua.bool-sample-conversion normal + nbthread 1 + lua-load "${testdir}/stream-order.lua" + + defaults + mode http + timeout connect "${HAPROXY_TEST_TIMEOUT-5s}" + timeout client "${HAPROXY_TEST_TIMEOUT-5s}" + timeout server "${HAPROXY_TEST_TIMEOUT-5s}" + + frontend fe + bind "fd@${fe}" + filter lua.a F + filter lua.b F + default_backend be + + backend be + filter-config lua id a enabled B + filter-config lua id b enabled B + filter-sequence request lua/b:before(lua/a) + server s2 ${s2_addr}:${s2_port} +} -start + +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-start == +} -run + +# And a instance-mode frontend can use a legacy backend. +server s3 { + rxreq + expect req.http.x-filter-order == "Fb:1;Fa:2;Ba:1;Bb:2;" + expect req.http.x-filter-start == + txresp +} -start + +haproxy h3 -conf { + global + tune.lua.bool-sample-conversion normal + nbthread 1 + lua-load "${testdir}/stream-order.lua" + + defaults + mode http + timeout connect "${HAPROXY_TEST_TIMEOUT-5s}" + timeout client "${HAPROXY_TEST_TIMEOUT-5s}" + timeout server "${HAPROXY_TEST_TIMEOUT-5s}" + + frontend fe + bind "fd@${fe}" + filter-config lua id a enabled F + filter-config lua id b enabled F + filter-sequence global lua/b:before(lua/a) + filter-sequence request lua/b:before(lua/a) + default_backend be + + backend be + filter lua.a B + filter lua.b B + server s3 ${s3_addr}:${s3_port} +} -start + +client c3 -connect ${h3_fe_sock} { + txreq + rxresp + expect resp.status == 200 + expect resp.http.x-filter-order == "Fa:2;Fb:1;Ba:1;Bb:2;" + expect resp.http.x-filter-start == +} -run + +# A listen proxy must not instantiate the same instances again as a backend. +server s4 { + rxreq + expect req.http.x-filter-order == "La:2;Lb:1;" + expect req.http.x-filter-start == + txresp +} -start + +haproxy h4 -conf { + global + tune.lua.bool-sample-conversion normal + nbthread 1 + lua-load "${testdir}/stream-order.lua" + + defaults + mode http + timeout connect "${HAPROXY_TEST_TIMEOUT-5s}" + timeout client "${HAPROXY_TEST_TIMEOUT-5s}" + timeout server "${HAPROXY_TEST_TIMEOUT-5s}" + + listen li + bind "fd@${li}" + filter-config lua id a enabled L + filter-config lua id b enabled L + filter-sequence global lua/b:before(lua/a) + filter-sequence response lua/b:before(lua/a) + server s4 ${s4_addr}:${s4_port} +} -start + +client c4 -connect ${h4_li_sock} { + txreq + rxresp + expect resp.status == 200 + expect resp.http.x-filter-order == "Lb:1;La:2;" + expect resp.http.x-filter-start == +} -run