]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: filters: Add a global option to enforce the legacy filter api
authorChristopher Faulet <cf@haproxy.x>
Thu, 8 Oct 2026 13:38:46 +0000 (15:38 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 9 Oct 2026 14:17:36 +0000 (16:17 +0200)
A proxy without explicit filter declaration uses the new filter mode by
default. It is an issue for the obsolete filters relying on an implicit
declaration only and not supporting the new mode: the implicit declarations
of such filters are not visible for the new mode, they are silently
ignored. All the filters supported in-tree are available in both modes, but
it is not necessarily the case of the third-party filters, and there was no
way for the user to restore the prior behavior in this case.

Add the "filter-api" global option for this: when it is set to
"downgrade-to-legacy", the legacy filter mode is enforced on the proxies
without explicit filter declaration, "filter" or "filter-*" directives. The
proxies with explicit declarations keep the mode they selected, so both
modes can be used simultaneously, on different proxies.

The option is unset by default. It can be set to "default" to restore the
default mode.

reg-tests/filters/legacy-mode.vtc [new file with mode: 0644]
src/filters.c

diff --git a/reg-tests/filters/legacy-mode.vtc b/reg-tests/filters/legacy-mode.vtc
new file mode 100644 (file)
index 0000000..b42c0fb
--- /dev/null
@@ -0,0 +1,55 @@
+varnishtest "Enforce the legacy filter mode on the proxies without declaration"
+
+# Regression test for the "filter-api downgrade-to-legacy" global option:
+# the proxies without explicit filter declaration must use the legacy filter
+# mode, the ones with an explicit "filter-*" declaration must keep the new mode.
+
+feature ignore_unknown_macro
+
+server s1 {
+    rxreq
+    txresp -hdr "Content-Type: text/plain" -bodylen 500
+} -repeat 2 -start
+
+haproxy h1 -conf {
+    global
+        filter-api downgrade-to-legacy
+
+    defaults
+        mode http
+        timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    # No filter declaration: with the legacy filter mode enforced globally,
+    # the implicit declaration of the compression filter is handled in the
+    # legacy mode, it must be operational.
+    listen li-implicit
+        bind "fd@${li1}"
+        compression algo gzip
+        compression type text/plain
+        server s1 ${s1_addr}:${s1_port}
+
+    # Explicit "filter-config" declaration: the new mode selected by the
+    # declaration always wins over the mode enforced globally, and the
+    # explicit instance replaces the implicit one. It is not enabled, so
+    # the compression is not performed.
+    listen li-explicit
+        bind "fd@${li2}"
+        filter-config http-comp-res
+        compression algo gzip
+        compression type text/plain
+        server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c-implicit -connect ${h1_li1_sock} {
+    txreq -url /implicit -hdr "Accept-Encoding: gzip"
+    rxresp
+    expect resp.http.content-encoding == gzip
+} -run
+
+client c-explicit -connect ${h1_li2_sock} {
+    txreq -url /explicit -hdr "Accept-Encoding: gzip"
+    rxresp
+    expect resp.http.content-encoding == <undef>
+} -run
index 278e9e20eccefec60512aae7cf3d653cfaeebf5e..59f46049f0cf421c54f39c66815f146788e2fb38 100644 (file)
@@ -62,6 +62,9 @@ static struct list res_filter_classes = LIST_HEAD_INIT(res_filter_classes);
 /* Used to be sure internal filters classes are initialized before any other ones */
 static int filter_classes_initialized = 0;
 
+/* If set, downgrade the filter API to the legacy mode by default (with implicit declarations only) */
+static int filter_api_downgraded = 0;
+
 /* Pool used to allocate filters */
 DECLARE_STATIC_TYPED_POOL(pool_head_filter, "filter", struct filter);
 
@@ -2515,6 +2518,35 @@ static int parse_filter_sequence(char **args, int section_type, struct proxy *cu
 
 }
 
+/* Parser for the "filter-api" keyword */
+static int parse_global_filter_api(char **args, int section_type,
+                                  struct proxy *curpx, const struct proxy *defpx,
+                                  const char *file, int line, char **err)
+{
+
+       if (too_many_args(1, args, err, NULL))
+               goto err;
+
+       if (*(args[1]) == 0) {
+               memprintf(err, "'%s' : expects 'default' or 'downgrade-to-legacy' as argument.\n", args[0]);
+               goto err;
+       }
+
+       if (strcmp(args[1], "default") == 0)
+               filter_api_downgraded = 0;
+       else if (strcmp(args[1], "downgrade-to-legacy") == 0)
+               filter_api_downgraded = 1;
+       else {
+               memprintf(err, "'%s' : unknown argument '%s' (expects 'default' or 'downgrade-to-legacy')",
+                         args[0], args[1]);
+               goto err;
+       }
+
+       return 0;
+  err:
+       return -1;
+}
+
 /* Note: must not be declared <const> as its list will be overwritten.
  * Please take care of keeping this list alphabetically sorted, doing so helps
  * all code contributors.
@@ -2527,6 +2559,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
                { CFG_LISTEN, "filter-disable", parse_filter_enable },
                { CFG_LISTEN, "filter-enable", parse_filter_enable },
                { CFG_LISTEN, "filter-sequence", parse_filter_sequence },
+               { CFG_GLOBAL, "filter-api", parse_global_filter_api },
                { 0, NULL, NULL },
        }
 };
@@ -2854,12 +2887,34 @@ static int flt_precheck_instances(struct proxy *proxy)
 }
 
 
+/* Applies the default filter mode on the proxy <px>: when the legacy filter
+ * mode was requested globally, a proxy with no explicit filter declaration
+ * (neither "filter" nor "filter-*" directives) uses the legacy mode instead
+ * of the new one, so the implicit declarations of the filters not supporting
+ * the new mode keep working. A proxy with explicit declarations keeps the
+ * mode it selected: the legacy mode for the "filter" directives, the new
+ * one for the "filter-*" directives.
+ */
+static void flt_apply_default_mode(struct proxy *px)
+{
+       if (filter_api_downgraded == 1 &&
+           !flt_use_legacy_filter(px) && !flt_has_explicit_config(px))
+               px->flags |= PR_FL_FILTER_LEGACY;
+}
+
 /* Calls flt_precheck_instances() for all proxies, see above */
 static int flt_precheck_instances_all()
 {
        struct proxy *px;
        int err_code = ERR_NONE;
 
+       /* Apply the globally requested mode before finalizing the filter
+        * instances, so the proxies using the legacy mode by default keep
+        * their instances as metadata only.
+        */
+       list_for_each_entry(px, &main_proxies, el)
+               flt_apply_default_mode(px);
+
        list_for_each_entry(px, &main_proxies, el) {
                if (px->flags & (PR_FL_DISABLED|PR_FL_STOPPED))
                        continue;