]> git.kaiwu.me - haproxy.git/commitdiff
REGTESTS: filters: Exercise TCP Lua payload chains and sequencing
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 2 Oct 2026 19:21:42 +0000 (21:21 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 9 Oct 2026 14:17:36 +0000 (16:17 +0200)
Use same-length, non-commuting Lua substitutions to observe the actual
TCP request and response evaluation order without depending on packet
boundaries. Mix the rewrites with trace and active bandwidth filters.

Inherit independent channel sequences from named defaults, reverse them
locally by first detaching the inherited child to the class anchor, and
check class-wide disabling followed by id-specific enabling. Include a
fully disabled Lua chain and a legacy chain retaining symmetric order.

Use HTTP framing only to check complete bodies over TCP; the proxies do
not parse HTTP and the Lua filters are not HTX-capable.

reg-tests/filters/lua-tcp-rewrite.lua [new file with mode: 0644]
reg-tests/filters/lua-tcp.vtc [new file with mode: 0644]

diff --git a/reg-tests/filters/lua-tcp-rewrite.lua b/reg-tests/filters/lua-tcp-rewrite.lua
new file mode 100644 (file)
index 0000000..4093982
--- /dev/null
@@ -0,0 +1,33 @@
+-- Same-length, non-commuting substitutions make channel order observable on
+-- the wire. No packet boundaries or transport timing enter the assertions.
+local function register(name, from, to)
+    local F = { id = name, flags = 0, from = from, to = to }
+
+    function F:new()
+        return setmetatable({}, { __index = self })
+    end
+
+    function F:start_analyze(txn, chn)
+        filter.register_data_filter(self, chn)
+        return filter.CONTINUE
+    end
+
+    function F:tcp_payload(txn, chn)
+        local count = chn:input()
+        if count == 0 then
+            return 0
+        end
+        local data = chn:data(0, count)
+        local replaced = data:gsub(self.from, self.to)
+        assert(chn:set(replaced, 0, count) == count)
+        return count
+    end
+
+    core.register_filter(name, F, function(conf, args)
+        return conf
+    end)
+end
+
+register("rewrite-a", "a", "b")
+register("rewrite-b", "b", "c")
+register("rewrite-c", "c", "a")
diff --git a/reg-tests/filters/lua-tcp.vtc b/reg-tests/filters/lua-tcp.vtc
new file mode 100644 (file)
index 0000000..3f09bc6
--- /dev/null
@@ -0,0 +1,127 @@
+varnishtest "TCP Lua payload chains honor inherited per-side sequences and state"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(LUA)'"
+feature ignore_unknown_macro
+
+# HTTP framing is only a convenient way to check complete bodies over TCP.
+# The proxies do not parse HTTP and the Lua filters have no HTX flag.
+server s1 {
+    rxreq
+    expect req.body == "cccccc"
+    txresp -body "aabbcc"
+} -start
+
+server s2 {
+    rxreq
+    expect req.body == "bbbbcc"
+    txresp -body "aabbcc"
+} -start
+
+server s3 {
+    rxreq
+    expect req.body == "aaaabb"
+    txresp -body "aabbcc"
+} -start
+
+server s4 {
+    rxreq
+    expect req.body == "bbbbcc"
+    txresp -body "aabbcc"
+} -start
+
+haproxy h1 -conf {
+    global
+        nbthread 1
+        tune.lua.bool-sample-conversion normal
+        lua-load "${testdir}/lua-tcp-rewrite.lua"
+    defaults plain
+        mode tcp
+        timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    frontend ordered from plain
+        bind "fd@${ordered}"
+        default_backend ordered_be
+    frontend reversed from plain
+        bind "fd@${reversed}"
+        default_backend reversed_be
+    frontend off from plain
+        bind "fd@${off}"
+        default_backend off_be
+    frontend legacy from plain
+        bind "fd@${legacy}"
+        default_backend legacy_be
+
+    defaults rewrites from plain
+        filter-config trace id trace enabled quiet
+        filter-config lua id rewrite-a enabled
+        filter-config lua id rewrite-b enabled
+        filter-config lua id rewrite-c enabled
+        filter-disable lua/rewrite-c
+        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/rewrite-b:before(lua/rewrite-a)
+        filter-sequence request lua/rewrite-a:before(lua/rewrite-b)
+        filter-sequence response lua/rewrite-b:before(lua/rewrite-a)
+
+    backend ordered_be from rewrites
+        tcp-request content set-bandwidth-limit upload
+        tcp-response content set-bandwidth-limit download
+        server s1 ${s1_addr}:${s1_port}
+
+    backend reversed_be from rewrites
+        filter-disable lua
+        filter-enable lua/rewrite-a lua/rewrite-b
+        # Detach the inherited child to the class anchor before reversing
+        # the relation. Moving its parent under it directly creates a cycle.
+        filter-sequence request lua/rewrite-a:before(lua) lua/rewrite-b:before(lua/rewrite-a)
+        filter-sequence response lua/rewrite-b:before(lua) lua/rewrite-a:before(lua/rewrite-b)
+        tcp-request content set-bandwidth-limit upload
+        tcp-response content set-bandwidth-limit download
+        server s2 ${s2_addr}:${s2_port}
+
+    backend off_be from rewrites
+        filter-disable lua
+        tcp-request content set-bandwidth-limit upload
+        tcp-response content set-bandwidth-limit download
+        server s3 ${s3_addr}:${s3_port}
+
+    backend legacy_be from plain
+        filter trace quiet
+        filter lua.rewrite-b
+        filter lua.rewrite-a
+        filter bwlim-in upload default-limit 1m default-period 1s
+        filter bwlim-out download default-limit 1m default-period 1s
+        tcp-request content set-bandwidth-limit upload
+        tcp-response content set-bandwidth-limit download
+        server s4 ${s4_addr}:${s4_port}
+} -start
+
+client c1 -connect ${h1_ordered_sock} {
+    txreq -req POST -body "aaaabb"
+    rxresp
+    expect resp.status == 200
+    expect resp.body == "bbcccc"
+} -run
+
+client c2 -connect ${h1_reversed_sock} {
+    txreq -req POST -body "aaaabb"
+    rxresp
+    expect resp.status == 200
+    expect resp.body == "cccccc"
+} -run
+
+client c3 -connect ${h1_off_sock} {
+    txreq -req POST -body "aaaabb"
+    rxresp
+    expect resp.status == 200
+    expect resp.body == "aabbcc"
+} -run
+
+client c4 -connect ${h1_legacy_sock} {
+    txreq -req POST -body "aaaabb"
+    rxresp
+    expect resp.status == 200
+    expect resp.body == "bbcccc"
+} -run