From: Willy Tarreau Date: Mon, 21 Sep 2026 20:35:35 +0000 (+0000) Subject: REGTESTS: http-rules: test replace-uri and the header replacement actions X-Git-Tag: v3.5-dev8~59 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=c0d0464cc1ffff55c25487bb5651c71058a7a6f9;p=haproxy.git REGTESTS: http-rules: test replace-uri and the header replacement actions replace-uri had no coverage at all, and replace-header and replace-value were only used by a test checking their size limits, not their semantics. The test rewrites a request target in the origin form and in the absolute form to show that replace-uri also sees the scheme and the authority, rewrites the scheme of an absolute URI, checks that a non matching regex leaves the target untouched, and compares the result with replace-path and replace-pathq on the same target. It also checks that replace-header replaces the whole header line while replace-value only replaces the matching comma-delimited element, and that back-references work in the replacement format. --- diff --git a/reg-tests/http-rules/replace_uri.vtc b/reg-tests/http-rules/replace_uri.vtc new file mode 100644 index 000000000..6f0427026 --- /dev/null +++ b/reg-tests/http-rules/replace_uri.vtc @@ -0,0 +1,127 @@ +varnishtest "Test the replace-uri action and the header replacement variants" + +# The replace-uri action had no coverage at all, and replace-header and +# replace-value were only used by a test checking their size limits, not +# their semantics. +# +# replace-uri acts on the whole request target, including the scheme and +# the authority when the request uses the absolute form, while +# replace-path and replace-pathq only see the path. replace-header acts +# on the whole header line while replace-value acts on each +# comma-delimited element separately. + +feature ignore_unknown_macro + +server s1 { + rxreq + txresp +} -repeat 9 -start + +haproxy h1 -conf { + global + .if feature(THREAD) + thread-groups 1 + .endif + chroot / + + 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}" + + # prefix the path, keeping the optional scheme and authority + http-request replace-uri ([^/:]*://[^/]*)?(.*) \1/pfx\2 if { url_param(t) -m str prefix } + + # rewrite the scheme of an absolute URI + http-request replace-uri ^http://(.*) https://\1 if { url_param(t) -m str scheme } + + # a non matching regex leaves the URI untouched + http-request replace-uri ^/nosuchprefix/(.*) /other/\1 if { url_param(t) -m str nomatch } + + # replace-path only sees the path, the query string is left aside + http-request replace-path (.*) /rp\1 if { url_param(t) -m str path } + http-request replace-pathq (.*) /rq\1 if { url_param(t) -m str pathq } + + http-request replace-header x-cache no-cache stale if { url_param(t) -m str hdr } + http-request replace-value x-cache no-cache stale if { url_param(t) -m str val } + http-request replace-header x-ref (a)-(b) \2-\1 if { url_param(t) -m str backref } + + # collect the result once all the rewriting rules have run + http-request set-var(txn.uri) url + http-request set-var(txn.cache) req.fhdr(x-cache) + http-request set-var(txn.ref) req.fhdr(x-ref) + + http-response set-header x-url "%[var(txn.uri)]" + http-response set-header x-cache "%[var(txn.cache)]" + http-response set-header x-ref "%[var(txn.ref)]" + + default_backend be + + backend be + server s1 ${s1_addr}:${s1_port} +} -start + +client c1 -connect ${h1_fe_sock} { + # origin form, the whole target is prefixed + txreq -url "/bar?t=prefix" + rxresp + expect resp.status == 200 + expect resp.http.x-url == "/pfx/bar?t=prefix" + + # absolute form, the scheme and the authority are preserved + txreq -url "http://www.example.com/bar?t=prefix" -hdr "Host: www.example.com" + rxresp + expect resp.status == 200 + expect resp.http.x-url == "http://www.example.com/pfx/bar?t=prefix" + + # the scheme itself may be rewritten + txreq -url "http://www.example.com/bar?t=scheme" -hdr "Host: www.example.com" + rxresp + expect resp.status == 200 + expect resp.http.x-url == "https://www.example.com/bar?t=scheme" + + txreq -url "/bar?t=nomatch" + rxresp + expect resp.status == 200 + expect resp.http.x-url == "/bar?t=nomatch" + + # replace-path ignores the query string, replace-pathq includes it + txreq -url "/bar?t=path" + rxresp + expect resp.status == 200 + expect resp.http.x-url == "/rp/bar?t=path" + + txreq -url "/bar?t=pathq" + rxresp + expect resp.status == 200 + expect resp.http.x-url == "/rq/bar?t=pathq" +} -run + +client c2 -connect ${h1_fe_sock} { + # replace-header matches the whole line, so a single occurrence of + # the pattern anywhere in it is replaced and the rest is dropped + txreq -url "/?t=hdr" -hdr "x-cache: no-cache, no-store" + rxresp + expect resp.status == 200 + expect resp.http.x-cache == "stale" +} -run + +client c3 -connect ${h1_fe_sock} { + # replace-value matches each comma-delimited element separately, so + # the other elements are preserved + txreq -url "/?t=val" -hdr "x-cache: no-cache, no-store" + rxresp + expect resp.status == 200 + expect resp.http.x-cache == "stale, no-store" +} -run + +client c4 -connect ${h1_fe_sock} { + txreq -url "/?t=backref" -hdr "x-ref: a-b" + rxresp + expect resp.status == 200 + expect resp.http.x-ref == "b-a" +} -run