From: Christopher Faulet Date: Thu, 8 Oct 2026 13:38:46 +0000 (+0200) Subject: MINOR: filters: Add a global option to enforce the legacy filter api X-Git-Tag: v3.5-dev9~54 X-Git-Url: https://git.kaiwu.me/$%7BGITURL%7D$%7Bcid%7D?a=commitdiff_plain;h=9324cd8d917e004ef2e6d3618188f6bf37010039;p=haproxy.git MINOR: filters: Add a global option to enforce the legacy filter api 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. --- diff --git a/reg-tests/filters/legacy-mode.vtc b/reg-tests/filters/legacy-mode.vtc new file mode 100644 index 000000000..b42c0fbb9 --- /dev/null +++ b/reg-tests/filters/legacy-mode.vtc @@ -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 == +} -run diff --git a/src/filters.c b/src/filters.c index 278e9e20e..59f46049f 100644 --- a/src/filters.c +++ b/src/filters.c @@ -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 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 : 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;