/* 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 */
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 *
* 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 */
--- /dev/null
+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
};
/* 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)
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;
}
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;
/* 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;
};
/* 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)
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;
}
/* 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;
goto err;
}
- LIST_APPEND(&curpx->filter_configs, &fconf->list);
+ flt_conf_add(curpx, fconf);
end:
return retval;
}
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;
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
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);
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) {
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;
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;
}
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;
}
}
/* 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.
*/
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)
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)
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
{
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)
}
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)
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)
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;
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)++;
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)
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)
}
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;
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;
}
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);