]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: filters: Inherit filter instances from defaults sections
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 30 Sep 2026 16:10:21 +0000 (18:10 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 9 Oct 2026 14:17:35 +0000 (16:17 +0200)
Deep-copy raw filter instances when a proxy references its defaults
section, before parsing the proxy's own directives. Preserve the class,
identifier, enabled state and source location, while marking the copy as
inherited so a later local declaration can replace it.

Add the helper that links a instance to its proxy's class references.
Copies own their arguments and are released independently of the defaults
proxy. On inheritance failure, release the partially copied instances.

include/haproxy/filters.h
src/filters.c
src/proxy.c

index 6b6b7bb79ce27c10429d146df5f36f0a79c7dda3..69808e271c1e389112a83c29bd0bc82d74715480 100644 (file)
@@ -129,6 +129,7 @@ extern struct filter_class flt_fcgi_cls;
 
 void flt_deinit(struct proxy *p);
 int  flt_check(struct proxy *p);
+int  flt_copy_instances(struct proxy *px, const struct proxy *defpx);
 void flt_free_instances(struct proxy *px);
 int  flt_init_class_refs(struct proxy *px);
 struct filter_instance *flt_find_instance(struct proxy *px, struct filter_class *cls, const char *id);
index 4adcafbcaeedf0897c83543089c6582f9971702c..a56c5c0d68e615c8e47a96df34f0ab5bc13783df 100644 (file)
@@ -1376,6 +1376,23 @@ static int flt_foreach_instance_side(struct proxy *px, unsigned int side,
        return 0;
 }
 
+/* Finds the class reference of the class <cls> in the proxy <px> for side
+ * <side>.
+ * Returns the class reference, or NULL if not found for the given side.
+ */
+static struct filter_class_ref *flt_get_class_ref(struct proxy *px, struct filter_class *cls,
+                                                 unsigned int side)
+{
+       struct list *refs = (side == FLT_SIDE_REQ ? &px->conf.filter_classes_req : &px->conf.filter_classes_res);
+       struct filter_class_ref *ref;
+
+       list_for_each_entry(ref, refs, list) {
+               if (ref->class == cls)
+                       return ref;
+       }
+       return NULL;
+}
+
 /* Context for flt_match_instance_cb() */
 struct flt_match_instance_ctx {
        struct filter_class *cls;   /* the class to match */
@@ -1450,6 +1467,41 @@ struct filter_instance *flt_find_instance(struct proxy *px, struct filter_class
 }
 
 
+/* Initializes the lists of the filter instance <inst> and links it in the
+ * class references of the proxy <px>, on each side the class is placed on.
+ * Returns 0 on success, -1 if the class is placed on no side (the
+ * instance cannot be evaluated).
+ */
+static int flt_init_instance(struct filter_instance *inst, struct proxy *px)
+{
+       struct filter_class_ref *ref;
+       int linked = 0;
+
+       inst->px = px;
+       LIST_INIT(&inst->req.reordered_before);
+       LIST_INIT(&inst->req.reordered_after);
+       LIST_INIT(&inst->req.list);
+       LIST_INIT(&inst->res.reordered_before);
+       LIST_INIT(&inst->res.reordered_after);
+       LIST_INIT(&inst->res.list);
+
+       if (LIST_INLIST(&inst->class->req.list)) {
+               ref = flt_get_class_ref(px, inst->class, FLT_SIDE_REQ);
+               if (!ref)
+                       return -1;
+               LIST_APPEND(&ref->instances, &inst->req.list);
+               linked = 1;
+       }
+       if (LIST_INLIST(&inst->class->res.list)) {
+               ref = flt_get_class_ref(px, inst->class, FLT_SIDE_RES);
+               if (!ref)
+                       return -1;
+               LIST_APPEND(&ref->instances, &inst->res.list);
+               linked = 1;
+       }
+       return linked ? 0 : -1;
+}
+
 /* Frees all filter instances of the proxy <px>, its filter class
  * references, its filter-enable entries and its filter sequences.
  */
@@ -1476,6 +1528,86 @@ void flt_free_instances(struct proxy *px)
 
 }
 
+/* Deep-copies the filter instances of the defaults proxy <defpx> into the
+ * proxy <px>, so that proxies inherit the filter instances of their
+ * defaults section. It must be called before any instance is added to <px>,
+ * so that locally defined ones can replace the inherited ones.
+ * Returns 0 on success, -1 on error (out of memory).
+ */
+static int flt_copy_instance(struct proxy *px, const struct filter_instance *inst)
+{
+       struct filter_instance *new_inst;
+       int i;
+
+       new_inst = calloc(1, sizeof(*new_inst));
+       if (!new_inst)
+               return -1;
+
+       new_inst->class   = inst->class;
+       new_inst->enabled = inst->enabled;
+       new_inst->flags   = inst->flags | FLT_INST_F_INHERITED;
+
+       if (inst->id) {
+               new_inst->id = strdup(inst->id);
+               if (!new_inst->id)
+                       goto error;
+       }
+       new_inst->conf.file = strdup(inst->conf.file);
+       if (!new_inst->conf.file)
+               goto error;
+       new_inst->conf.line = inst->conf.line;
+       new_inst->conf.argc = inst->conf.argc;
+       new_inst->conf.argv = calloc(inst->conf.argc + 1, sizeof(*new_inst->conf.argv));
+       if (!new_inst->conf.argv)
+               goto error;
+       for (i = 0; i < inst->conf.argc; i++) {
+               new_inst->conf.argv[i] = strdup(inst->conf.argv[i]);
+               if (!new_inst->conf.argv[i])
+                       goto error;
+       }
+
+       if (flt_init_instance(new_inst, px) < 0)
+               goto error;
+       return 0;
+
+  error:
+       flt_free_instance(new_inst);
+       return -1;
+}
+
+/* Copy all filter deinitions of the defaults proxy <defpx> into the proxy <px>.
+ * Returns 0 on success, -1 on error (out of memory).
+ */
+int flt_copy_instances(struct proxy *px, const struct proxy *defpx)
+{
+       struct filter_class_ref *ref;
+       struct filter_instance *inst;
+
+       /* iterate over all the instances of the defaults proxy: the request
+        * side first, then the instances of the classes with no request side
+        * from the response side
+        */
+       list_for_each_entry(ref, &defpx->conf.filter_classes_req, list) {
+               list_for_each_entry(inst, &ref->instances, req.list)
+                       if (flt_copy_instance(px, inst) < 0)
+                               goto error;
+       }
+       list_for_each_entry(ref, &defpx->conf.filter_classes_res, list) {
+               list_for_each_entry(inst, &ref->instances, res.list) {
+                       if (LIST_INLIST(&inst->req.list))
+                               continue; /* already copied from the request side */
+                       if (flt_copy_instance(px, inst) < 0)
+                               goto error;
+               }
+       }
+
+       return 0;
+
+  error:
+       flt_free_instances(px);
+       return -1;
+}
+
 
 /* Note: must not be declared <const> as its list will be overwritten.
  * Please take care of keeping this list alphabetically sorted, doing so helps
index bb187722b496b1fc8f9ba31a40b9218f13af6d1a..b5f3388de6200a70ce536d81421e882401fda68f 100644 (file)
@@ -3161,6 +3161,17 @@ int proxy_ref_defaults(struct proxy *px, struct proxy *defpx, char **errmsg)
                defaults_px_ref(defpx, px);
        }
 
+       /* The proxy inherits the filter instances of its defaults section. It
+        * happens before its own directives are parsed, so that locally defined
+        * ones can replace the inherited ones.
+        */
+       if (flt_copy_instances(px, defpx) < 0) {
+               memprintf(errmsg, "out of memory while inheriting filter instances from defaults section (at %s:%d)",
+                         defpx->conf.file, defpx->conf.line);
+               err_code |= ERR_ALERT | ERR_FATAL;
+               goto out;
+       }
+
        if (defpx->tcpcheck.rs && (defpx->tcpcheck.rs->flags & TCPCHK_RULES_PROTO_CHK) &&
            (px->cap & PR_CAP_LISTEN) == PR_CAP_BE) {
                /* If the current default proxy defines tcpcheck rules, the