]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: filters: initialize the filter classes before the register stage
authorMiroslav Zagorac <mzagorac@haproxy.com>
Fri, 16 Oct 2026 19:32:49 +0000 (21:32 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 19 Oct 2026 19:54:04 +0000 (21:54 +0200)
The filter classes of HAProxy are initialized from the register stage,
where the internal classes are registered too. A module registering its
own class from that stage is refused, since whether it runs before or
after this initialization depends on the link order, and the objects of an
addon are linked first: its classes are registered too early, always.

Initializing the classes from the prepare stage leaves the whole register
stage to the modules, which then register their classes in time for
"haproxy -vv" to report them, the filter classes being listed while the
arguments are parsed, between the register and the init stages.

Nothing else moves: the classes are initialized in the same order, and the
placement of a class still decides the evaluation order of the filters.
The filter documentation was updated to match.

doc/configuration.txt
doc/internals/api/filters.txt
src/filters.c

index 2f047285dbd7b3fa85a7ad9d57f551c4438e25ae..e79376453c5ba215267a9738489ca4227b58738d 100644 (file)
@@ -31514,7 +31514,8 @@ reading. Their sole purpose is to explain how to decipher them.
 
 Here are listed officially supported filters with the list of parameters they
 accept. Depending on compile options, some of these filters might be
-unavailable. The list of available filter classes is reported in haproxy -vv.
+unavailable. The list of available filter classes, including the ones added by
+the addons built into the binary, is reported in haproxy -vv.
 
 A filter is declared on a proxy in one of two modes:
 
index d52f414cf818f8e97972dd626c523126c4da0547..55522b7e42fb984e5ccc532855b4ed5448b1614a 100644 (file)
@@ -149,7 +149,9 @@ Note that additional parameters on the filter line must be parsed by the filter
 itself. The "filter" directive is only supported by filters registering a
 keyword with flt_register_keywords() (see section 3.2.2).
 
-The list of available filter classes is reported by 'haproxy -vv' :
+The list of available filter classes is reported by 'haproxy -vv'. It contains
+the built-in classes and the ones other modules register from the STG_REGISTER
+stage (see section 3.2) :
 
     $> haproxy -vv
     HAProxy version 1.7-dev2-3a1d4a-33 2016/03/21
@@ -547,7 +549,7 @@ it supports, relative to an already placed class, with filter_place_class() :
             return -1;
         return 0;
     }
-    INITCALL1(STG_INIT, my_filter_register_class);
+    INITCALL0(STG_REGISTER, my_filter_register_class);
 
 filter_place_class() accepts the three sides, FLT_SIDE_GLOBAL, FLT_SIDE_REQ
 and FLT_SIDE_RES, and may be called once per side :
@@ -575,9 +577,14 @@ startup because it would be useless. The placements define the default
 evaluation order of the instances of the class relative to the other classes,
 which may then be refined per proxy with "filter-sequence" (section 2.2).
 
-WARNING : filter_register_class() must be called after the built-in classes
-          were initialized, so from an initcall of the STG_INIT stage or a later
-          one. Otherwise the class registration fails.
+WARNING : filter_register_class() and filter_place_class() must be called after
+          the built-in classes were initialized at the STG_PREPARE stage. Call
+          them from an STG_REGISTER initcall. From STG_PREPARE, the result
+          depends on the link order, and an addon is linked first, so its
+          registration always fails. 'haproxy -vv' is handled right after the
+          STG_REGISTER stage and only lists the classes registered so far. A
+          class registered from a later stage still works and is listed by the
+          "show filter classes" CLI command, but not by 'haproxy -vv'.
 
 
 Then the filter internal configuration must be defined. For instance :
@@ -1717,9 +1724,10 @@ are left untouched.
    directive uses the keyword name.
 
 2. Register the class and place it on each side it supports, relative to an
-   already placed class. For a filter maintained out of the HAProxy tree,
-   use the public API from an initcall, because the registration must happen
-   after the built-in classes were initialized :
+   already placed class. For a filter maintained out of the HAProxy tree, use
+   the public API from an STG_REGISTER initcall. The built-in classes are
+   initialized before this stage, and 'haproxy -vv' only lists the classes
+   registered by its end (see section 3.2) :
 
     static int my_filter_register_class(void)
     {
@@ -1736,7 +1744,7 @@ are left untouched.
             return -1;
         return 0;
     }
-    INITCALL1(STG_INIT, my_filter_register_class);
+    INITCALL0(STG_REGISTER, my_filter_register_class);
 
    filter_register_class_full() registers the class and places it at the
    same position on both sides in a single call, which is enough for most
@@ -1868,11 +1876,12 @@ enabled by another directive, or is referenced by other directives.
     only drives the side-independent operations (configuration checks,
     initialization and destruction).
 
-  * A class must be registered after the built-in classes were initialized,
-    from a STG_INIT initcall or a later stage, otherwise the registration
-    fails. For a filter inside the HAProxy tree, the class is registered in
-    filter_init_classes() to explicitly define its default order with the
-    other internal classes.
+  * A class must be registered after the built-in classes were initialized, from
+    a STG_REGISTER initcall. From STG_PREPARE, the registration fails or
+    succeeds depending on the link order. A class registered after STG_REGISTER
+    works, but 'haproxy -vv' does not list it. For a filter inside the HAProxy
+    tree, the class is registered in filter_init_classes() to explicitly define
+    its default order with the other internal classes.
 
   * The instance id is optional for a class that does not set
     FLT_CLS_FL_MULTI: the parse callback may receive a NULL 'inst->id'. A
index ab1184b3d30f3cf186a249e9cea38a0710d5edef..ba5d1ae9f1f998bdb073e7b1a64455e7f21448b7 100644 (file)
@@ -3510,7 +3510,10 @@ int flt_parse_instance_legacy(struct proxy *px, struct filter_instance *inst, ch
  * not support the "filter-config" directive). The class is initialized and
  * added to the global registry of the known classes, and appended at the end
  * of the global order. It is not usable on any channel side until placed with
- * filter_place_class(). Must be called after filter_init_classes().
+ * filter_place_class(). Must be called after filter_init_classes(), which
+ * runs at the prepare stage: a class registered from the register stage on
+ * is added in time for "haproxy -vv" to list it, a class registered later
+ * is reported by the CLI alone.
  * Returns 0 on success, -1 on error (too early call, duplicate name).
  */
 int filter_register_class(struct filter_class *cls, const char *name,
@@ -3662,7 +3665,10 @@ int filter_register_class_full(struct filter_class *cls, const char *name,
 
 /* Init function responsible to initialize all intenral filter classes and to
  * insert them is global list above. The default evaluation order of all
- * internal filters is defined in this function.
+ * internal filters is defined in this function. It runs from the prepare stage
+ * so that a class registered by another module from the register stage is
+ * initialized in time: "haproxy -vv" reports the available filter classes from
+ * the argument parsing, which happens between the two.
  *
  * Exemple to register a new filter class:
  *
@@ -3683,7 +3689,7 @@ int filter_register_class_full(struct filter_class *cls, const char *name,
  *       return -1;
  *    return 0;
  * }
- * INITCALL1(STG_INIT, myfilter_register_class);
+ * INITCALL1(STG_REGISTER, myfilter_register_class);
  */
 static void filter_init_classes(void)
 {
@@ -3794,7 +3800,7 @@ REGISTER_POST_CHECK(flt_init_all);
 REGISTER_PER_THREAD_INIT(flt_init_all_per_thread);
 REGISTER_PER_THREAD_DEINIT(flt_deinit_all_per_thread);
 
-INITCALL0(STG_REGISTER, filter_init_classes);
+INITCALL0(STG_PREPARE, filter_init_classes);
 
 /*
  * Local variables: