From: Christopher Faulet Date: Mon, 19 Oct 2026 12:57:48 +0000 (+0200) Subject: MEDIUM: filters: automatically order the implicit core filters in legacy mode X-Git-Tag: v3.5-dev10~107 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=f9ecf6a93f18d5c21b250ea6f94110b298428321;p=haproxy.git MEDIUM: filters: automatically order the implicit core filters in legacy mode 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. --- diff --git a/include/haproxy/filters-t.h b/include/haproxy/filters-t.h index 3115b2dbf..56d9b4b8d 100644 --- a/include/haproxy/filters-t.h +++ b/include/haproxy/filters-t.h @@ -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 */ diff --git a/include/haproxy/filters.h b/include/haproxy/filters.h index 18a66f038..2ca8a5111 100644 --- a/include/haproxy/filters.h +++ b/include/haproxy/filters.h @@ -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 to the three configuration + * lists of the proxy : 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 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 at the beginning of the + * three configuration lists of the proxy . + */ +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 * diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h index 1063b2eec..7dbe026ee 100644 --- a/include/haproxy/proxy-t.h +++ b/include/haproxy/proxy-t.h @@ -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 index 000000000..7e1b4537f --- /dev/null +++ b/reg-tests/filters/legacy-implicit-order.vtc @@ -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 diff --git a/src/cache.c b/src/cache.c index 83173a87b..8967eb42b 100644 --- a/src/cache.c +++ b/src/cache.c @@ -670,8 +670,8 @@ struct cache_store_check_ctx { }; /* Checks the filter 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. 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; diff --git a/src/fcgi-app.c b/src/fcgi-app.c index 0034e3027..fdfd09b14 100644 --- a/src/fcgi-app.c +++ b/src/fcgi-app.c @@ -216,8 +216,8 @@ struct fcgi_flt_check_ctx { }; /* Checks the fcgi filter 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; diff --git a/src/filters.c b/src/filters.c index b244aa63b..cd3a82817 100644 --- a/src/filters.c +++ b/src/filters.c @@ -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 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 for the order + * : 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 all the filter configs pending in the ordering + * buckets , 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 + * 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 for each filter configuration of the proxy in the evaluation - * order of 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 (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. * 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) diff --git a/src/flt_decomp.c b/src/flt_decomp.c index bad2f8785..871947941 100644 --- a/src/flt_decomp.c +++ b/src/flt_decomp.c @@ -1348,10 +1348,13 @@ struct decomp_check_ctx { int decomp_req_seen; int decomp_res_seen; int cache_seen; - int explicit; }; -/* Checks the filter against the decomp filter being checked: ... +/* Checks the filter 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; diff --git a/src/flt_http_comp.c b/src/flt_http_comp.c index c911b759e..15aa16610 100644 --- a/src/flt_http_comp.c +++ b/src/flt_http_comp.c @@ -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 against the compression filter being checked: ... +/* Checks the filter 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; diff --git a/src/haload.c b/src/haload.c index be1c3b1aa..487574f2e 100644 --- a/src/haload.c +++ b/src/haload.c @@ -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; } diff --git a/src/proxy.c b/src/proxy.c index c44999813..00d8925ab 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -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);