From 6ae02b5ccbd9c3ac4c0db2cfc1789cc1ada4e54a Mon Sep 17 00:00:00 2001 From: IcyJerry <1795701794@qq.com> Date: Mon, 12 Oct 2026 18:49:24 +0800 Subject: [PATCH] httpcaddyfile: keep source order for vars with non-comparable matchers (#8197) Use i/j swap instead of !sortByPath for vars, so matchers that can't be compared keep their Caddyfile order. Path matchers still sort with the more specific one later. Fixes the vars ordering part of #8187. --- caddyconfig/httpcaddyfile/directives.go | 19 +++--- .../map_and_vars_with_raw_types.caddyfiletest | 8 +-- ...rs_named_matchers_keep_order.caddyfiletest | 63 +++++++++++++++++++ 3 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 caddytest/integration/caddyfile_adapt/sort_vars_named_matchers_keep_order.caddyfiletest diff --git a/caddyconfig/httpcaddyfile/directives.go b/caddyconfig/httpcaddyfile/directives.go index f411aabe3..e8ad62d22 100644 --- a/caddyconfig/httpcaddyfile/directives.go +++ b/caddyconfig/httpcaddyfile/directives.go @@ -465,6 +465,15 @@ func sortRoutes(routes []ConfigValue, order []string) { return dirPositions[iDir] < dirPositions[jDir] } + // some directives involve setting values which can overwrite + // each other, so it makes most sense to reverse the order so + // that the least-specific matcher is first, allowing the last + // matching one to win; we swap i and j instead of using !, + // so matchers that can't be compared keep their order + if iDir == "vars" { + i, j = j, i + } + // directives are the same; sub-sort by path matcher length if there's // only one matcher set and one path (this is a very common case and // usually -- but not always -- helpful/expected, oh well; user can @@ -535,15 +544,7 @@ func sortRoutes(routes []ConfigValue, order []string) { return len(iRoute.MatcherSetsRaw) > 0 && len(jRoute.MatcherSetsRaw) == 0 }() - // some directives involve setting values which can overwrite - // each other, so it makes most sense to reverse the order so - // that the least-specific matcher is first, allowing the last - // matching one to win - if iDir == "vars" { - return !sortByPath - } - - // everything else is most-specific matcher first + // most-specific matcher first (or last for vars, see above) return sortByPath }) } diff --git a/caddytest/integration/caddyfile_adapt/map_and_vars_with_raw_types.caddyfiletest b/caddytest/integration/caddyfile_adapt/map_and_vars_with_raw_types.caddyfiletest index 8b872635d..6c9ade715 100644 --- a/caddytest/integration/caddyfile_adapt/map_and_vars_with_raw_types.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/map_and_vars_with_raw_types.caddyfiletest @@ -101,16 +101,16 @@ vars { ], "source": "{http.request.host}" }, + { + "foo": "bar", + "handler": "vars" + }, { "abc": true, "def": 1, "ghi": 2.3, "handler": "vars", "jkl": "mn op" - }, - { - "foo": "bar", - "handler": "vars" } ] } diff --git a/caddytest/integration/caddyfile_adapt/sort_vars_named_matchers_keep_order.caddyfiletest b/caddytest/integration/caddyfile_adapt/sort_vars_named_matchers_keep_order.caddyfiletest new file mode 100644 index 000000000..724804180 --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/sort_vars_named_matchers_keep_order.caddyfiletest @@ -0,0 +1,63 @@ +:80 + +@first header X-Test a +@second header X-Test a +@third header X-Other b + +vars @first value first +vars @second value second +vars @third value third +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":80" + ], + "routes": [ + { + "match": [ + { + "header": { + "X-Test": [ + "a" + ] + } + } + ], + "handle": [ + { + "handler": "vars", + "value": "first" + }, + { + "handler": "vars", + "value": "second" + } + ] + }, + { + "match": [ + { + "header": { + "X-Other": [ + "b" + ] + } + } + ], + "handle": [ + { + "handler": "vars", + "value": "third" + } + ] + } + ] + } + } + } + } +}