]> git.kaiwu.me - haproxy.git/commitdiff
MEDIUM: filters: automatically order the implicit core filters in legacy mode
authorChristopher Faulet <cfaulet@haproxy.com>
Mon, 19 Oct 2026 12:57:48 +0000 (14:57 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 20 Oct 2026 09:13:30 +0000 (11:13 +0200)
In the legacy filter mode, the implicitly declared core filters (the
decompression, cache, compression and fcgi filters) are now automatically
ordered instead of relying on the user to declare them in a valid position
or reporting a configuration error.

The forced global order, used for the side-independent operations
(configuration checks, initialization and destruction), is:

  decomp-req > decomp-res > cache-store >  any other filters > http-comp-req > http-comp-res > fcgi

Two additional orders are maintained for the request and the response
channels, matching the three independent orders of the new instance mode,
and used to fill the stream's request and response filter lists and by
flt_foreach_conf() for the FLT_SIDE_REQ and FLT_SIDE_RES sides:

  * request:  decomp-req > any other filter > http-comp-res > decomp-res > http-comp-req > fcgi

  * response: fcgi > decomp-res > cache-store > any other filter > decomp-req > http-comp-res

A new FLT_CFG_FL_EXPLICIT flag marks the filters declared with the legacy
"filter" directive. Explicitly declared core filters are never moved and act
as segment boundaries: implicitly declared filters are sorted inside the
segment they appear in, so they never cross an explicitly declared one. The
request and response orders are stored in two new proxy lists,
filters_req_configs and filters_res_configs, filled with the 'by_req' and
'by_res' members of the filter configurations.

The filter configurations are now linked in the three lists at declaration
time, before flt_check() reorders them, so the lookups performed by the
rules during the configuration checks (the cache rules looking for the
cache-store filter on the response side) behave the same as before the
reordering.

Because the position of implicit filters no longer depends on the
declaration order, the errors reported when mixing an explicit filter with
implicitly declared decompression, compression, cache or fcgi filters are
removed. Explicitly declaring core filters in a conflicting order still
reports an error, the manual placement remains the user's responsibility
for explicit declarations.

The ordering was validated functionally: with an explicit filter mixed
with implicitly declared core filters, the response body is decompressed
and stored uncompressed in the cache before the trace and compression
filters see it, and a gzipped request is decompressed before reaching the
explicit filters.

include/haproxy/filters-t.h
include/haproxy/filters.h
include/haproxy/proxy-t.h
reg-tests/filters/legacy-implicit-order.vtc [new file with mode: 0644]
src/cache.c
src/fcgi-app.c
src/filters.c
src/flt_decomp.c
src/flt_http_comp.c
src/haload.c
src/proxy.c

index 3115b2dbf9e5af5176ae0b29ee1d86661456b563..56d9b4b8d40cb6832f84a48b351ae0a3ed099d2c 100644 (file)
@@ -26,6 +26,7 @@
 
 /* Flags set on a filter config */
 #define FLT_CFG_FL_HTX    0x00000001  /* The filter can filter HTX streams */
+#define FLT_CFG_FL_EXPLICIT 0x00000002 /* Explicitly declared with the legacy "filter" directive */
 
 /* Flags set on a stream filter instance */
 #define FLT_FL_IS_BACKEND_FILTER  0x0001 /* The filter is a backend filter */
index 18a66f038a8852373b4275490202fb88feb9f690..2ca8a5111b0ae17066693510e2eb60e1a4281beb 100644 (file)
@@ -206,6 +206,29 @@ static inline int flt_use_legacy_filter(const struct proxy *px)
        return px->flags & PR_FL_FILTER_LEGACY;
 }
 
+/* Appends the legacy filter config <fconf> to the three configuration
+ * lists of the proxy <px>: the global order and the request and response
+ * orders, in declaration order. flt_check() later reorders the three
+ * lists with flt_reorder_implicit_conf(). The 'list', 'by_req' and
+ * 'by_res' members of <fconf> must not be linked yet.
+ */
+static inline void flt_conf_add(struct proxy *px, struct flt_conf *fconf)
+{
+       LIST_APPEND(&px->filter_configs, &fconf->list);
+       LIST_APPEND(&px->filters_req_configs, &fconf->by_req);
+       LIST_APPEND(&px->filters_res_configs, &fconf->by_res);
+}
+
+/* Same as flt_conf_add() but inserts <fconf> at the beginning of the
+ * three configuration lists of the proxy <px>.
+ */
+static inline void flt_conf_insert(struct proxy *px, struct flt_conf *fconf)
+{
+       LIST_INSERT(&px->filter_configs, &fconf->list);
+       LIST_INSERT(&px->filters_req_configs, &fconf->by_req);
+       LIST_INSERT(&px->filters_res_configs, &fconf->by_res);
+}
+
 /* Helper function that returns the "global" state of filters attached to a
  * stream. */
 static inline struct strm_flt *
index 1063b2eec63f0c24aaaadca094ea3a936877fef1..7dbe026eec260b34957e1e70f0a4400ebf130832 100644 (file)
@@ -534,6 +534,9 @@ struct proxy {
                                                 * name is used
                                                 */
        struct list filter_configs;             /* list of the filters that are declared on this proxy */
+       struct list filters_req_configs;        /* legacy: filter configs in request evaluation order */
+       struct list filters_res_configs;        /* legacy: filter configs in response evaluation order */
+
        struct list filter_instances;           /* flat list of all filter instances, in global order */
        struct list filter_req_instances;       /* flat list of the filter instances evaluated on the request side */
        struct list filter_res_instances;       /* flat list of the filter instances evaluated on the response side */
diff --git a/reg-tests/filters/legacy-implicit-order.vtc b/reg-tests/filters/legacy-implicit-order.vtc
new file mode 100644 (file)
index 0000000..7e1b453
--- /dev/null
@@ -0,0 +1,78 @@
+varnishtest "Automatically order implicitly declared filters in the legacy mode"
+
+# Regression test for the automatic ordering of the implicitly declared
+# filters in the legacy filter mode. When an explicit filter (here "trace")
+# is declared together with implicitly declared core filters, the implicit
+# filters are automatically placed at their canonical position instead of
+# reporting a configuration error: the decompression filters come first,
+# then the cache filters, then the other filters in their declaration order,
+# then the compression filters and finally the fcgi filter.
+#
+# Before the automatic ordering, this configuration was rejected:
+#   proxy 'li1': require an explicit filter declaration to use HTTP response
+#                compression
+#   proxy 'li1': require an explicit filter declaration to use the cache
+# The ordering is validated functionally: the first response is stored
+# uncompressed in the cache (before the compression filter) and the second
+# one is served from the cache.
+
+feature ignore_unknown_macro
+
+server s1 {
+    rxreq
+    txresp -hdr "Content-Type: text/plain" -hdr "Cache-Control: max-age=20" \
+           -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}"
+
+    cache my_cache
+        total-max-size 1
+        max-age 20
+
+    listen li1
+        bind "fd@${li1}"
+        # Explicit filter mixed with implicitly declared core filters:
+        # previously a configuration error, now automatically ordered with
+        # the cache filter before the trace filter and the compression
+        # filter after it.
+        filter trace quiet
+        compression algo gzip
+        compression type text/plain
+        http-request cache-use my_cache
+        http-response cache-store my_cache
+        http-response set-header X-Hit %[res.cache_hit]
+        server s1 ${s1_addr}:${s1_port}
+} -start
+
+# First request: the response is stored in the cache then compressed for the
+# client. The cache filter must be evaluated before the compression filter,
+# otherwise the compressed body would be stored.
+client c1 -connect ${h1_li1_sock} {
+    txreq -url /a -hdr "Accept-Encoding: gzip"
+    rxresp
+    expect resp.status == 200
+    expect resp.http.content-encoding == gzip
+    expect resp.http.x-hit == 0
+    gunzip
+} -run
+
+# Second request: served from the cache. A cache hit is only possible if the
+# uncompressed body was stored, proving the cache filter was evaluated
+# before the compression filter. And the body is still delivered
+# uncompressed, confirming the stored body was not the compressed one.
+client c2 -connect ${h1_li1_sock} {
+    txreq -url /a -hdr "Accept-Encoding: gzip"
+    rxresp
+    expect resp.status == 200
+    expect resp.http.x-hit == 1
+    expect resp.bodylen == 500
+} -run
index 83173a87b7cb718116b20f96edcb094b8a393980..8967eb42bae9a8b71708b3b5f29934c935aae043 100644 (file)
@@ -670,8 +670,8 @@ struct cache_store_check_ctx {
 };
 
 /* Checks the filter <f> against the cache filter being checked: the
- * compression must be evaluated after the cache, and an implicit cache
- * declaration is only allowed with the compression and fcgi filters.
+ * compression must be evaluated after the cache. Implicit filters are
+ * automatically ordered, so no error is reported for them.
  * Returns non-zero to stop the iteration on error, 0 otherwise.
  */
 static int cache_store_check_order(struct flt_conf *f, void *data)
@@ -709,14 +709,6 @@ static int cache_store_check_order(struct flt_conf *f, void *data)
        else if (f->id == fcgi_flt_id)
                return 0;
 #endif
-       else if ((f->id != ctx->fconf->id) && ctx->cconf->flags & CACHE_FLT_F_IMPLICIT_DECL) {
-               /* Implicit declaration is only allowed with the compression,
-                * decompression, fcgi and other cache filter. For other
-                * filters, an implicit declaration is required. */
-               ha_alert("config: %s '%s': require an explicit filter declaration "
-                        "to use the cache '%s'.\n", proxy_type_str(ctx->px), ctx->px->id, ctx->cache->id);
-               return 1;
-       }
        return 0;
 }
 
@@ -2403,8 +2395,7 @@ static int check_cache_rule(struct act_rule *rule, struct proxy *proxy, char **e
        fconf->id = cache_store_flt_id;
        fconf->conf = cconf;
        fconf->ops  = &cache_ops;
-
-       LIST_APPEND(&proxy->filter_configs, &fconf->list);
+       flt_conf_add(proxy, fconf);
 
        rule->arg.act.p[0] = cconf;
        return 1;
@@ -3657,6 +3648,8 @@ parse_cache_flt(char **args, int *cur_arg, struct proxy *px,
 
                /* Remove the implicit filter. <cconf> is kept for the explicit one */
                LIST_DELETE(&f->list);
+               LIST_DELETE(&f->by_req);
+               LIST_DELETE(&f->by_res);
                free(f);
                free(name);
                break;
index 0034e302705b8350bae36dccbdfdb80bbb128b30..fdfd09b1483e77e7b27f579bb3e3ec77032ade4e 100644 (file)
@@ -216,8 +216,8 @@ struct fcgi_flt_check_ctx {
 };
 
 /* Checks the fcgi filter <f> against the fcgi filter being checked. There must
- * be only one fcgi-app per backend, and in legacy mode, an implicit fcgi
- * declaration is only allowed with the compression and cache filters.
+ * be only one fcgi-app per backend. The position of implicitly declared
+ * filters is automatically ordered, no error is reported for them.
  * Returns -1 on error, 0 otherwise.
  */
 static int fcgi_flt_check_one(struct flt_conf *f, void *data)
@@ -235,23 +235,6 @@ static int fcgi_flt_check_one(struct flt_conf *f, void *data)
                return -1;
        }
 
-       /* Legacy filters not used for this proxy, we can continue */
-       if (!flt_use_legacy_filter(ctx->px))
-               return 0;
-
-       /* In legacy mode, implicit declaration is only allowed with the
-        * compression, decompression and cache. For other filters, an explicit
-        * declaration is required. */
-       if (f->id == http_comp_req_flt_id  || f->id == http_comp_res_flt_id ||
-           f->id == decomp_req_flt_id  || f->id == decomp_res_flt_id ||
-           f->id == cache_store_flt_id)
-               return 0;
-       if (ctx->fcgi_conf->flags & FCGI_FLT_F_IMPLICIT_DECL) {
-               ha_alert("proxy '%s': require an explicit filter declaration "
-                        "to use the fcgi-app '%s'.\n", ctx->px->id, ctx->fcgi_conf->name);
-               return -1;
-       }
-
        return 0;
 }
 
@@ -559,6 +542,8 @@ parse_fcgi_flt(char **args, int *cur_arg, struct proxy *px,
 
                /* Place the filter at its right position */
                LIST_DELETE(&f->list);
+               LIST_DELETE(&f->by_req);
+               LIST_DELETE(&f->by_res);
                free(f);
                ha_free(&name);
                break;
@@ -691,7 +676,7 @@ static int proxy_parse_use_fcgi_app(char **args, int section, struct proxy *curp
                goto err;
        }
 
-       LIST_APPEND(&curpx->filter_configs, &fconf->list);
+       flt_conf_add(curpx, fconf);
 
   end:
        return retval;
index b244aa63b871df67939841e37c1292ab2ed469a8..cd3a82817b6394d3b23df30cdfa48a67573e8e3c 100644 (file)
@@ -568,7 +568,8 @@ parse_filter(char **args, int section_type, struct proxy *curpx,
                }
 
                curpx->flags |= PR_FL_FILTER_LEGACY;
-               LIST_APPEND(&curpx->filter_configs, &fconf->list);
+               fconf->flags |= FLT_CFG_FL_EXPLICIT;
+               flt_conf_add(curpx, fconf);
        }
        return 0;
 
@@ -687,6 +688,182 @@ flt_init_all_per_thread()
        return 1;
 }
 
+/* Returns 1 if the filter config <fconf> is one of the core filters whose
+ * position is automatically ordered when they are implicitly declared on
+ * a proxy (the decompression, cache, compression and fcgi filters), 0
+ * otherwise.
+ */
+static int flt_is_core_flt(const struct flt_conf *fconf)
+{
+       return (fconf->id == decomp_req_flt_id || fconf->id == decomp_res_flt_id ||
+               fconf->id == cache_store_flt_id ||
+               fconf->id == http_comp_req_flt_id || fconf->id == http_comp_res_flt_id
+#if defined(USE_FCGI)
+               || fconf->id == fcgi_flt_id
+#endif
+              );
+}
+
+/* Returns the ordering class of the filter config <fconf> for the order
+ * <side>: the implicit core filters are ordered as follows
+ *
+ *   global:   decomp-req > decomp-res > cache-store > any other filter >
+ *             http-comp-req > http-comp-res > fcgi
+ *
+ *   request:  decomp-req > any other filter > http-comp-res > decomp-res >
+ *             http-comp-req > fcgi
+ *
+ *   response: fcgi > decomp-res > cache-store > any other filter >
+ *             decomp-req > http-comp-res
+ *
+ * Explicitly declared core filters are never moved and act as segment
+ * boundaries (see flt_reorder_implicit_conf()).
+ */
+static int flt_order_class(const struct flt_conf *fconf, unsigned int side)
+{
+       int is_comp_req = (fconf->id == http_comp_req_flt_id);
+       int is_comp_res = (fconf->id == http_comp_res_flt_id);
+       int is_decomp_req = (fconf->id == decomp_req_flt_id);
+       int is_decomp_res = (fconf->id == decomp_res_flt_id);
+       int is_cache = (fconf->id == cache_store_flt_id);
+#if defined(USE_FCGI)
+       int is_fcgi = (fconf->id == fcgi_flt_id);
+#endif
+
+       switch (side) {
+       case FLT_SIDE_REQ:
+               if (is_decomp_req)
+                       return 0;
+               if (is_comp_res)
+                       return 2;
+               if (is_decomp_res)
+                       return 3;
+               if (is_comp_req)
+                       return 4;
+#if defined(USE_FCGI)
+               if (is_fcgi)
+                       return 5;
+#endif
+               return 1; /* any other filter */
+
+       case FLT_SIDE_RES:
+#if defined(USE_FCGI)
+               if (is_fcgi)
+                       return 0;
+#endif
+               if (is_decomp_res)
+                       return 1;
+               if (is_cache)
+                       return 2;
+               if (is_decomp_req)
+                       return 4;
+               if (is_comp_res)
+                       return 5;
+               return 3; /* any other filter */
+
+       default:
+               /* global order */
+               if (is_decomp_req || is_decomp_res)
+                       return 0;
+               if (is_cache)
+                       return 1;
+               if (is_comp_req || is_comp_res)
+                       return 3;
+#if defined(USE_FCGI)
+               if (is_fcgi)
+                       return 4;
+#endif
+               return 2; /* any other filter */
+       }
+}
+
+/* Appends at the end of <out> all the filter configs pending in the ordering
+ * buckets <seg>, in class order, and empties the buckets.
+ */
+static void flt_flush_order_segs(struct list *out, struct list seg[], int nb_seg)
+{
+       int i;
+
+       for (i = 0; i < nb_seg; i++) {
+               while (!LIST_ISEMPTY(&seg[i])) {
+                       struct list *el = seg[i].n;
+
+                       LIST_DEL_INIT(el);
+                       LIST_APPEND(out, el);
+               }
+       }
+}
+
+/* Reorders the implicitly declared core filter configs of the legacy proxy <px>
+ * to force the canonical filter orders. Three independent orders are filled,
+ * respectively with the 'list', 'by_req' and 'by_res' members of the filter
+ * configs
+ *
+ * The global order (px->filter_configs) is used for the side-independent
+ * operations (configuration checks, initialization and destruction). The
+ * request and response orders (px->filters_req_configs and
+ * px->filters_res_configs) are used to fill the stream's request and response
+ * filter lists.
+ *
+ * Explicitly declared core filters are never moved, they keep their declaration
+ * order. They delimit segments of filters inside which the implicitly declared
+ * core filters are sorted, so that an implicit filter never crosses an
+ * explicitly declared one.
+ */
+static void flt_reorder_implicit_conf(struct proxy *px)
+{
+       struct flt_conf *fconf, *back;
+       struct list out, seg[3][6];
+       int i, s;
+
+       for (s = 0; s < 3; s++) {
+               for (i = 0; i < 6; i++)
+                       LIST_INIT(&seg[s][i]);
+       }
+
+       /* First, the global order, with the 'list' member. */
+       LIST_INIT(&out);
+       list_for_each_entry_safe(fconf, back, &px->filter_configs, list) {
+               if ((fconf->flags & FLT_CFG_FL_EXPLICIT) && flt_is_core_flt(fconf)) {
+                       /* Explicitly declared core filter: flush the current
+                        * segment before it and keep the filter in place.
+                        */
+                       flt_flush_order_segs(&out, seg[0], 6);
+                       LIST_DEL_INIT(&fconf->list);
+                       LIST_APPEND(&out, &fconf->list);
+                       continue;
+               }
+               LIST_DEL_INIT(&fconf->list);
+               LIST_APPEND(&seg[0][flt_order_class(fconf, FLT_SIDE_GLOBAL)], &fconf->list);
+       }
+       flt_flush_order_segs(&out, seg[0], 6);
+       LIST_SPLICE(&px->filter_configs, &out);
+
+       /* Then the request and response orders, from the final global order,
+        * with the 'by_req' and 'by_res' members. Both lists are rebuilt from
+        * scratch: reset their heads, all the links were initialized by
+        * flt_conf_add()/flt_conf_insert() or above.
+        */
+       LIST_INIT(&px->filters_req_configs);
+       LIST_INIT(&px->filters_res_configs);
+       list_for_each_entry_safe(fconf, back, &px->filter_configs, list) {
+               if ((fconf->flags & FLT_CFG_FL_EXPLICIT) && flt_is_core_flt(fconf)) {
+                       /* Explicitly declared core filter: flush the current
+                        * segments before it and keep the filter in place.
+                        */
+                       flt_flush_order_segs(&px->filters_req_configs, seg[1], 6);
+                       flt_flush_order_segs(&px->filters_res_configs, seg[2], 6);
+                       LIST_APPEND(&px->filters_req_configs, &fconf->by_req);
+                       LIST_APPEND(&px->filters_res_configs, &fconf->by_res);
+                       continue;
+               }
+               LIST_APPEND(&seg[1][flt_order_class(fconf, FLT_SIDE_REQ)], &fconf->by_req);
+               LIST_APPEND(&seg[2][flt_order_class(fconf, FLT_SIDE_RES)], &fconf->by_res);
+       }
+       flt_flush_order_segs(&px->filters_req_configs, seg[1], 6);
+       flt_flush_order_segs(&px->filters_res_configs, seg[2], 6);
+}
+
 /*
  * Calls 'check' callback for all filters attached to a proxy. This happens
  * after the configuration parsing but before filters initialization. Returns
@@ -703,6 +880,7 @@ flt_check(struct proxy *proxy)
 
        err += check_implicit_decomp_flt(proxy);
        err += check_implicit_http_comp_flt(proxy);
+       flt_reorder_implicit_conf(proxy);
        list_for_each_entry(fconf, &proxy->filter_configs, list) {
                if (fconf->ops->check)
                        err += fconf->ops->check(proxy, fconf);
@@ -755,6 +933,8 @@ flt_deinit(struct proxy *proxy)
                if (fconf->ops->deinit)
                        fconf->ops->deinit(proxy, fconf);
                LIST_DELETE(&fconf->list);
+               LIST_DELETE(&fconf->by_req);
+               LIST_DELETE(&fconf->by_res);
                free(fconf);
        }
        list_for_each_entry(inst, &proxy->filter_instances, global.list) {
@@ -806,12 +986,12 @@ flt_deinit_all_per_thread()
                flt_deinit_per_thread(px);
 }
 
-/* Attach a filter in global stream order. In legacy mode, also append both
- * channel links immediately. Instance mode fills them in independent passes.
+/* Attach a filter in global stream order. The channel lists are filled by
+ * the caller, from the proxy's per-side orders.
  * Returns -1 on error, 0 on success (including filters declining attachment).
  */
 static int
-flt_stream_add_filter(struct stream *s, struct flt_conf *fconf, unsigned int flags, int legacy)
+flt_stream_add_filter(struct stream *s, struct flt_conf *fconf, unsigned int flags)
 {
        struct filter *f;
 
@@ -838,11 +1018,6 @@ flt_stream_add_filter(struct stream *s, struct flt_conf *fconf, unsigned int fla
 
        LIST_APPEND(&strm_flt(s)->filters, &f->list);
 
-       if (legacy) {
-               LIST_APPEND(&s->req.flt.filters, &f->req_list);
-               LIST_APPEND(&s->res.flt.filters, &f->res_list);
-       }
-
        strm_flt(s)->flags |= STRM_FLT_FL_HAS_FILTERS;
        return 0;
 }
@@ -861,17 +1036,37 @@ static int flt_stream_add_proxy_filters(struct stream *s, struct proxy *px, unsi
        struct filter *filter;
 
        if (flt_use_legacy_filter(px)) {
+               /* The attach callbacks are called in the global order, then
+                * the stream's request and response filter lists are filled
+                * with the proxy's request and response orders.
+                */
                list_for_each_entry(fconf, &px->filter_configs, list) {
-                       if (flt_stream_add_filter(s, fconf, flags, 1) < 0)
+                       if (flt_stream_add_filter(s, fconf, flags) < 0)
                                return -1;
                }
+               list_for_each_entry(fconf, &px->filters_req_configs, by_req) {
+                       list_for_each_entry(filter, &strm_flt(s)->filters, list) {
+                               if (filter->config == fconf) {
+                                       LIST_APPEND(&s->req.flt.filters, &filter->req_list);
+                                       break;
+                               }
+                       }
+               }
+               list_for_each_entry(fconf, &px->filters_res_configs, by_res) {
+                       list_for_each_entry(filter, &strm_flt(s)->filters, list) {
+                               if (filter->config == fconf) {
+                                       LIST_APPEND(&s->res.flt.filters, &filter->res_list);
+                                       break;
+                               }
+                       }
+               }
                return 0;
        }
 
        list_for_each_entry(inst, &px->filter_instances, global.list) {
                if (!inst->enabled || !inst->fconf)
                        continue;
-               if (flt_stream_add_filter(s, inst->fconf, flags, 0) < 0)
+               if (flt_stream_add_filter(s, inst->fconf, flags) < 0)
                        return -1;
        }
 
@@ -1807,8 +2002,9 @@ struct filter_instance *flt_find_instance(struct proxy *px, struct filter_class
 }
 
 /* Calls <fct> for each filter configuration of the proxy <px> in the evaluation
- * order of side <side> (FLT_SIDE_GLOBAL, FLT_SIDE_REQ or FLT_SIDE_RES). if the
- * proxy is in legacy mode (PR_FL_FILTER_LEGACY) the filter_configs list is used.
+ * order of side <side> (FLT_SIDE_GLOBAL, FLT_SIDE_REQ or FLT_SIDE_RES). In
+ * legacy mode (PR_FL_FILTER_LEGACY), the matching filter_configs,
+ * filters_req_configs or filters_res_configs list is used.
  * <fct> must return 0 to continue, any other value to stop immediately; that
  * value is then returned.
  */
@@ -1820,6 +2016,22 @@ int flt_foreach_conf(struct proxy *px, unsigned int side,
        int ret = 0;
 
        if (flt_use_legacy_filter(px)) {
+               if (side == FLT_SIDE_REQ) {
+                       list_for_each_entry(fconf, &px->filters_req_configs, by_req) {
+                               ret = fct(fconf, data);
+                               if (ret)
+                                       break;
+                       }
+                       return ret;
+               }
+               if (side == FLT_SIDE_RES) {
+                       list_for_each_entry(fconf, &px->filters_res_configs, by_res) {
+                               ret = fct(fconf, data);
+                               if (ret)
+                                       break;
+                       }
+                       return ret;
+               }
                list_for_each_entry(fconf, &px->filter_configs, list) {
                        ret = fct(fconf, data);
                        if (ret)
index bad2f87859f922cf99650c1b417053cd0f2e7952..8719479417942d2ca5435b9cffcf97f30fa21882 100644 (file)
@@ -1348,10 +1348,13 @@ struct decomp_check_ctx {
        int decomp_req_seen;
        int decomp_res_seen;
        int cache_seen;
-       int explicit;
 };
 
-/* Checks the filter <f> against the decomp filter being checked: ...
+/* Checks the filter <f> against the decomp filter being checked. It is used
+ * to know if a decompression filter is already declared and to report an
+ * explicitly declared decompression filter on the response placed after a
+ * cache filter. Implicit filters are automatically ordered, so no error
+ * is reported for them.
  * Returns non-zero to stop the iteration on error, 0 otherwise.
  */
 static int decomp_check_order(struct flt_conf *f, void *data)
@@ -1371,23 +1374,9 @@ static int decomp_check_order(struct flt_conf *f, void *data)
                ctx->decomp_res_seen = 1;
                return 0;
        }
-       /* Legacy filters not used for this proxy, we can return */
-       if (!flt_use_legacy_filter(ctx->px))
-               return 0;
-
-       if (f->id == cache_store_flt_id) {
+       if (flt_use_legacy_filter(ctx->px) && f->id == cache_store_flt_id)
                ctx->cache_seen = 1;
-               return 0;
-       }
-       if (f->id == http_comp_req_flt_id || f->id == http_comp_res_flt_id)
-               return 0;
-#if defined(USE_FCGI)
-       if (f->id == fcgi_flt_id)
-               return 0;
-#endif
-       ctx->explicit = 1;
        return 0;
-
 }
 
 int
@@ -1395,7 +1384,7 @@ check_implicit_decomp_flt(struct proxy *proxy)
 {
        struct flt_conf *fconf;
        struct decomp_check_ctx ctx = { .px = proxy, .decomp_req_seen = 0, .decomp_res_seen = 0,
-                                       .cache_seen = 0, .explicit = 0};
+                                       .cache_seen = 0};
        int err = 0;
 
        if (proxy->decomp == NULL)
@@ -1407,14 +1396,9 @@ check_implicit_decomp_flt(struct proxy *proxy)
        }
 
        if ((proxy->cap & PR_CAP_FE) && proxy->decomp->req.algos && !ctx.decomp_req_seen) {
-               if (ctx.explicit) {
-                       ha_alert("config: %s '%s': require an explicit 'filter decomp-req' declaration to use "
-                                "decompression.\n", proxy_type_str(proxy), proxy->id);
-                       err++;
-                       goto end;
-               }
                /* Implicit declaration of the decomp-req filter should be at
-                * the beginning of the filter list.
+                * the beginning of the filter list. It will be placed at its
+                * final position by flt_reorder_implicit_conf().
                 */
                fconf = calloc(1, sizeof(*fconf));
                if (!fconf)
@@ -1422,17 +1406,12 @@ check_implicit_decomp_flt(struct proxy *proxy)
                fconf->id   = decomp_req_flt_id;
                fconf->conf = &proxy->decomp->req;
                fconf->ops  = &decomp_req_ops;
-               LIST_INSERT(&proxy->filter_configs, &fconf->list);
+               flt_conf_insert(proxy, fconf);
        }
        if ((proxy->cap & PR_CAP_BE) && proxy->decomp->res.algos && !ctx.decomp_res_seen) {
-               if (ctx.explicit) {
-                       ha_alert("config: %s '%s': require an explicit 'filter decomp-res' declaration to use "
-                                "decompression.\n", proxy_type_str(proxy), proxy->id);
-                       err++;
-                       goto end;
-               }
                /* Implicit declaration of the decomp-res filter should be at
-                * the beginning of the filter list.
+                * the beginning of the filter list. It will be placed at its
+                * final position by flt_reorder_implicit_conf().
                 */
                fconf = calloc(1, sizeof(*fconf));
                if (!fconf)
@@ -1440,7 +1419,7 @@ check_implicit_decomp_flt(struct proxy *proxy)
                fconf->id   = decomp_res_flt_id;
                fconf->conf = &proxy->decomp->res;
                fconf->ops  = &decomp_res_ops;
-               LIST_INSERT(&proxy->filter_configs, &fconf->list);
+               flt_conf_insert(proxy, fconf);
        }
  end:
        return err;
index c911b759e21845be09e4d69a262cc69419cb7479..15aa16610ace6be2b0d67d2f00e9c0c2099e8a95 100644 (file)
@@ -1113,11 +1113,12 @@ parse_http_comp_flt(char **args, int *cur_arg, struct proxy *px,
        fconf_res->name = "comp-res";
        fconf_res->conf = comp;
        fconf_res->ops = &comp_res_ops;
+       fconf_res->flags = FLT_CFG_FL_EXPLICIT;
 
        /* manually add the fconf_res to the list because filter API doesn't
         * know about it
         */
-       LIST_APPEND(&px->filter_configs, &fconf_res->list);
+       flt_conf_add(px, fconf_res);
 
 
        (*cur_arg)++;
@@ -1247,10 +1248,13 @@ struct http_comp_check_ctx {
        struct proxy *px;
        int comp_req_seen;
        int comp_res_seen;
-       int explicit;
 };
 
-/* Checks the filter <f> against the compression filter being checked: ...
+/* Checks the filter <f> against the compression filters being checked. It
+ * is used to know if a compression filter is already declared and to report
+ * an explicitly declared cache filter placed after a compression filter on
+ * the response. Implicit filters are automatically ordered, so no error is
+ * reported for them.
  * Returns non-zero to stop the iteration on error, 0 otherwise.
  */
 static int http_comp_check_order(struct flt_conf *f, void *data)
@@ -1265,35 +1269,21 @@ static int http_comp_check_order(struct flt_conf *f, void *data)
                ctx->comp_res_seen = 1;
                return 0;
        }
-       /* Legacy filters not used for this proxy, we can return */
-       if (!flt_use_legacy_filter(ctx->px))
-               return 0;
-
-       if (f->id == cache_store_flt_id) {
-               if (ctx->comp_res_seen) {
-                       ha_alert("config: %s '%s': unable to enable the compression filter on response "
-                                "before any cache filter.\n",
-                                proxy_type_str(ctx->px), ctx->px->id);
-                       return 1;
-               }
-               return 0;
+       if (flt_use_legacy_filter(ctx->px) && f->id == cache_store_flt_id &&
+           ctx->comp_res_seen) {
+               ha_alert("config: %s '%s': unable to enable the compression filter on response "
+                        "before any cache filter.\n",
+                        proxy_type_str(ctx->px), ctx->px->id);
+               return 1;
        }
-       if (f->id == decomp_req_flt_id || f->id == decomp_res_flt_id)
-               return 0;
-#if defined(USE_FCGI)
-       if (f->id == fcgi_flt_id)
-               return 0;
-#endif
-       ctx->explicit = 1;
        return 0;
-
 }
 
 int
 check_implicit_http_comp_flt(struct proxy *proxy)
 {
        struct flt_conf *fconf;
-       struct http_comp_check_ctx ctx = { .px = proxy, .comp_req_seen = 0, .comp_res_seen = 0, .explicit = 0};
+       struct http_comp_check_ctx ctx = { .px = proxy, .comp_req_seen = 0, .comp_res_seen = 0};
        int err = 0;
 
        if (proxy->comp == NULL)
@@ -1305,38 +1295,30 @@ check_implicit_http_comp_flt(struct proxy *proxy)
        }
 
        if ((proxy->comp->flags & COMP_FL_DIR_REQ) && !ctx.comp_req_seen) {
-               if (ctx.explicit) {
-                       ha_alert("config: %s '%s': require an explicit filter declaration to use "
-                                "HTTP request compression\n", proxy_type_str(proxy), proxy->id);
-                       err++;
-                       goto end;
-               }
-               /* Implicit declaration of the request compression filter is always the last
-                * one */
+               /* Implicit declaration of the request compression filter is always the
+                * last one. Its final position is forced by
+                * flt_reorder_implicit_conf().
+                */
                fconf = calloc(1, sizeof(*fconf));
                if (!fconf)
                        goto out_of_memory;
                fconf->id   = http_comp_req_flt_id;
                fconf->conf = proxy->comp;
                fconf->ops  = &comp_req_ops;
-               LIST_APPEND(&proxy->filter_configs, &fconf->list);
+               flt_conf_add(proxy, fconf);
        }
        if ((proxy->comp->flags & COMP_FL_DIR_RES) && !ctx.comp_res_seen) {
-               if (ctx.explicit) {
-                       ha_alert("config: %s '%s': require an explicit filter declaration to use "
-                                "HTTP response compression\n", proxy_type_str(proxy), proxy->id);
-                       err++;
-                       goto end;
-               }
-               /* Implicit declaration of the response compression filter is always the last
-                * one */
+               /* Implicit declaration of the response compression filter is always the
+                * last one. Its final position is forced by
+                * flt_reorder_implicit_conf().
+                */
                fconf = calloc(1, sizeof(*fconf));
                if (!fconf)
                        goto out_of_memory;
                fconf->id   = http_comp_res_flt_id;
                fconf->conf = proxy->comp;
                fconf->ops  = &comp_res_ops;
-               LIST_APPEND(&proxy->filter_configs, &fconf->list);
+               flt_conf_add(proxy, fconf);
        }
  end:
        return err;
index be1c3b1aa87cee7626dfa4268312c7d60764f034..487574f2e898e31dc41e392a5be942f3ba77d96e 100644 (file)
@@ -2433,7 +2433,7 @@ static int hld_cfg_finalize(void)
                fconf->id = fcgi_flt_id;
                fconf->conf = fcgi_conf;
                fconf->ops = &fcgi_flt_ops;
-               LIST_APPEND(&hld_proxy.filter_configs, &fconf->list);
+               flt_conf_add(&hld_proxy, fconf);
 
                hld_proxy.flags |= PR_FL_FILTER_LEGACY;
        }
index c44999813f20fecd1d586a8a8bdca7f5d23c093b..00d8925ab238040364960b782264dc56df7eb893 100644 (file)
@@ -1759,6 +1759,8 @@ void init_new_proxy(struct proxy *p)
        LIST_INIT(&p->conf.args.list);
        LIST_INIT(&p->conf.lf_checks);
        LIST_INIT(&p->filter_configs);
+       LIST_INIT(&p->filters_req_configs);
+       LIST_INIT(&p->filters_res_configs);
        LIST_INIT(&p->filter_instances);
        LIST_INIT(&p->filter_req_instances);
        LIST_INIT(&p->filter_res_instances);