]> git.kaiwu.me - haproxy.git/commitdiff
DOC: filters: Document the filter instance mode and the legacy mode
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 6 Oct 2026 13:54:54 +0000 (15:54 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 9 Oct 2026 14:17:36 +0000 (16:17 +0200)
The filter API was reworked to support filter instances declared with
"filter-config" and referring to a filter class, while the historical
"filter" directive is still supported through the legacy mode. Update the
documentation accordingly.

In the internal API documentation, define the new wording: a "filter
instance" is the configuration entity declared in a proxy, which is
user-visible, and a "stream filter instance" is the per-stream entity
attached to a stream, represented by 'struct filter' in the code.

Explain the two declaration modes and how a proxy selects one of them: the
explicit declarations select the mode and a proxy using only implicit
filters works in the new mode, unless the "filter-api" global option is set
to "downgrade-to-legacy". Describe how to write a filter for the new mode:
the filter class, its registration and placement, the instance parse
callback and its constraints. In particular, the instances are finalized
after the whole configuration was parsed, their configuration is shared and
read-only, they are disabled by default and they may be implicit or
inherited. Explain how the legacy mode is kept working and how the parsing
code can be shared between both modes with flt_parse_instance_legacy().

Update the descriptions of the filter configuration and instance structures,
document the helpers used to reference a filter configuration from other
directives (flt_has_filter(), flt_foreach_conf() and flt_find_conf()) and
add the FAQ section announced by the summary.

In the configuration manual, document the "filter-config", "filter-enable",
"filter-disable" and "filter-sequence" directives, their interaction with
the legacy "filter" directive, the two declaration modes, the
"legacy-filter-mode" global option enforcing the legacy mode on the proxies
without explicit declaration, and the "filter-config" syntax of every
supported filter. In the management manual, document the "show filter
classes", "show filter instance" and "show filter instances" CLI commands.

doc/configuration.txt
doc/internals/api/filters.txt
doc/management.txt

index b814e5bae288185fad4d81480a6364e147865c94..b80479f48905563c8b4473cbc5589e33da36b95f 100644 (file)
@@ -1811,6 +1811,7 @@ The following keywords are supported in the "global" section :
    - expose-deprecated-directives
    - expose-experimental-directives
    - external-check
+   - filter-api
    - fd-hard-limit
    - gid
    - grace
@@ -2644,6 +2645,25 @@ external-check [preserve-env]
   exposes the check program to potential attacks. See "option external-check",
   and "insecure-fork-wanted", and "insecure-setuid-wanted" for extra details.
 
+filter-api default | downgrade-to-legacy
+  Set the default filter API to use for a proxy when no filter directives are
+  used ("filter", "filter-config", "filter-enable", "filter-disable" or
+  "filter-sequence").
+
+  This option is unset by default. So proxies without explicit filter
+  directives rely on the filter instance api. By setting it to
+  "downgrade-to-legacy", it is possible to force proxies to use the legacy
+  filter API by default. It could be useful the time to convert outdated
+  filters still relying on the legacy API only. Indeed, such filters will be
+  silently ignored except if the "filter" directive is used to declare them.
+  This option can be set to "default" to restore the default behavior.
+
+  Note that it should be a temporary workaround. There is no warranty that all
+  filters will remain compatible with the legacy API. In that case, they will
+  be silently ignored if the filter API is downgraded.
+
+  See section 9.
+
 fd-hard-limit <number>
   Sets an upper bound to the maximum number of file descriptors that the
   process will use, regardless of system limits. While "ulimit-n" and "maxconn"
@@ -6251,6 +6271,10 @@ external-check path                       X          -         X         X
 force-persist                             -          -         X         X
 force-be-switch                           -          X         X         -
 filter                                    -          X         X         X
+filter-config                             X (!)      X         X         X
+filter-disable                            X (!)      X         X         X
+filter-enable                             X (!)      X         X         X
+filter-sequence                           X (!)      X         X         X
 fullconn                                  X          -         X         X
 guid                                      -          X         X         X
 hash-balance-factor                       X          -         X         X
@@ -8282,8 +8306,15 @@ filter <name> [param*]
   Multiple occurrences of the filter line can be used for the same proxy. The
   same filter can be referenced many times if needed.
 
+  Using this directive selects the legacy filter declaration mode for the
+  proxy. It must not be used together with "filter-config", "filter-enable",
+  "filter-disable" or "filter-sequence" in the same proxy, including when
+  these directives are inherited from a defaults section. Conversely, this
+  directive is rejected in a proxy inheriting filter instances from its
+  defaults section. See "filter-config" for the new declaration mode.
+
   Example:
-      listen
+      listen li
         bind *:80
 
         filter trace name BEFORE-HTTP-COMP
@@ -8295,7 +8326,203 @@ filter <name> [param*]
 
         server srv1 192.168.0.1:80
 
-  See also : section 9.
+  See also : "filter-config", "filter-enable", "filter-disable",
+             "filter-sequence" and section 9.
+
+
+filter-config <class> [id <id>] [enabled] [<parameters>]
+  Declare a filter instance of the class <class> attached to the proxy.
+
+  May be used in the following contexts: tcp, http
+
+  May be used in sections :   defaults | frontend | listen | backend
+                                yes(!) |    yes   |   yes  |   yes
+
+  Arguments :
+    <class>      is the filter class name. The supported classes are listed
+                 in section 9.
+
+    <id>        declares the instance identifier. It is mandatory for filter
+                 classes allowing several instances in the same proxy (the id
+                 must be unique for its class) and optional otherwise. Ids
+                 may only contain alphanumeric characters, '.', '_' and '-'.
+                 In the other "filter-*" directives and in the CLI, an
+                 instance is referred to as <class>/<id>.
+
+    enabled      activates the filter instance. Without it, the instance is
+                 declared but disabled and will not be instantiated on
+                 streams. It can be activated later with the "filter-enable"
+                 directive.
+
+    <parameters> are the parameters accepted by the filter class, as
+                 documented in section 9.
+
+  The optional "id <id>" and "enabled" tokens must come first, in this order,
+  before the filter parameters.
+
+  This directive is only available from named defaults sections, not anonymous
+  ones.
+
+  A filter instance is the configuration of a filter in the proxy, shared by
+  all the streams on which it is instantiated. The directive may be used
+  several times for the same proxy, including to declare several instances of
+  the same class when the class allows it.
+
+  The instances declared in a defaults section are inherited by the proxies
+  using this section. A local instance with the same class and id replaces the
+  inherited one. Likewise, an explicit instance replaces an instance
+  implicitly declared by another directive, for instance by the "compression"
+  options or the "cache-store" rules. The evaluation order of the instances
+  may be inspected with the "show filter instances" CLI command and adjusted
+  with the "filter-sequence" directive.
+
+  Using this directive selects the new filter declaration mode for the proxy.
+  It must not be used together with the legacy "filter" directive in the same
+  proxy. A defaults section defining filter instances may not be inherited by
+  a "listen" section, nor by both frontends and backends.
+
+  Example:
+      listen li
+        bind *:80
+
+        filter-config trace id BEFORE-HTTP-COMP enabled
+        filter-config http-comp-req enabled
+        filter-config http-comp-res enabled
+        filter-config trace id AFTER-HTTP-COMP enabled
+        filter-sequence response trace/AFTER-HTTP-COMP:after(http-comp-res)
+
+        compression algo gzip
+        compression offload
+
+        server srv1 192.168.0.1:80
+
+  See also : "filter", "filter-enable", "filter-disable", "filter-sequence"
+             and section 9.
+
+
+filter-disable <class>[/<id>] [<class>[/<id>] ...]
+  Disable the filter instances matching the arguments.
+
+  May be used in the following contexts: tcp, http
+
+  May be used in sections :   defaults | frontend | listen | backend
+                                yes(!) |    yes   |   yes  |   yes
+
+  Arguments :
+    <class>[/<id>]  references a filter instance. Without an id, all the
+                    instances of the filter class are concerned.
+
+  This directive is only available from named defaults sections, not anonymous
+  ones.
+
+  All the referenced instances must exist at the end of the configuration
+  parsing, otherwise an error is reported. An instance may be declared after
+  the directive enabling or disabling it. When several "filter-enable" and
+  "filter-disable" directives reference the same instance, the last one wins.
+
+  A disabled instance is not instantiated on streams. Its state is reported by
+  the "show filter instance" CLI command. See "filter-config" and section 9.
+
+  Example:
+      defaults def-fe
+        filter-config trace id TR1 enabled
+        filter-config trace id TR2 enabled
+
+      frontend fe from def-fe
+        bind *:80
+        filter-disable trace/TR2
+
+
+  See also : "filter-config", "filter-enable"
+
+
+filter-enable <class>[/<id>] [<class>[/<id>] ...]
+  Enable the filter instances matching the arguments.
+
+  May be used in the following contexts: tcp, http
+
+  May be used in sections :   defaults | frontend | listen | backend
+                                yes(!) |    yes   |   yes  |   yes
+
+  Arguments :
+    <class>[/<id>]  references a filter instance. Without an id, all the
+                    instances of the filter class are concerned.
+
+  This directive is only available from named defaults sections, not anonymous
+  ones.
+
+  All the referenced instances must exist at the end of the configuration
+  parsing, otherwise an error is reported. An instance may be declared after
+  the directive enabling or disabling it. When several "filter-enable" and
+  "filter-disable" directives reference the same instance, the last one wins.
+
+  An enabled instance is instantiated on the streams of the proxy, unless the
+  filter itself declines it or it is not supported, for instance a filter
+  requiring HTX on a TCP stream. See "filter-config" and section 9.
+
+  Example :
+      defaults def-fe
+        filter-config trace id TR1
+        filter-config trace id TR2
+
+      frontend fe from def-fe
+        bind *:80
+        filter-enable trace
+
+  See also : "filter-config", "filter-disable"
+
+
+filter-sequence {global|request|response} <instance>:before(<entity>) [<instance>:after(<entity>) ...]
+  Change the evaluation order of the filter instances of the proxy.
+
+  May be used in the following contexts: tcp, http
+
+  May be used in sections :   defaults | frontend | listen | backend
+                                yes(!) |    yes   |   yes  |   yes
+
+  Arguments :
+    {global|request|response}  is the order to update. The request and
+                               response orders define the evaluation order of
+                               the filters on the corresponding channel and
+                               may be changed independently. The global order
+                               only drives the side-independent operations
+                               (configuration checks, initialization and
+                               destruction); changing it does not change the
+                               request or the response order.
+
+    <instance>                 is the filter instance to move, as
+                               <class>[/<id>]. An id must be given if the
+                               class of the instance allows several instances
+                               in the proxy.
+
+    before(<entity>)           moves the instance before the entity.
+    after(<entity>)            moves the instance after the entity.
+
+    <entity>                   is a filter class or a filter instance, as
+                               <class>[/<id>]. When a class is referenced, all
+                               its instances are concerned. The reference
+                               class must be placed on the selected side and
+                               the referenced instance, if any, must exist.
+
+  This directive is only available from named defaults sections, not anonymous
+  ones.
+
+  By default, the instances are evaluated following the order of their class
+  on the selected side (see section 9). This directive changes this order.
+  Several sequences may be given in the same directive and the directive may
+  be used several times; the sequences are applied in configuration order. A
+  sequence moving an instance that does not exist is ignored. Sequences
+  creating a loop are rejected.
+
+  Example :
+      listen
+        bind *:80
+        filter-config trace id T1 enabled
+        filter-config trace id T2 enabled
+        filter-sequence response trace/T2:before(trace/T1)
+        server srv1 192.168.0.1:80
+
+  See also : "filter-config", "filter-enable", "filter-disable" and section 9.
 
 
 fullconn <conns>
@@ -31250,21 +31477,67 @@ 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 filters is reported in haproxy -vv.
-
-See also : "filter"
+unavailable. The list of available filter classes is reported in haproxy -vv.
+
+A filter is declared on a proxy in one of two modes:
+
+  * The legacy mode relies on the "filter" directive. Each "filter" line
+    declares an independent filter configuration and the declaration order
+    defines the evaluation order. This mode is kept for backward
+    compatibility.
+
+  * The new mode relies on the "filter-config" directive. Each declaration is
+    a filter instance, attached to its filter class. The evaluation order is
+    defined by the class placement and can be adjusted per proxy with the
+    "filter-sequence" directive. The instances are disabled by default and
+    are activated with the "enabled" token or the "filter-enable" directive.
+
+The mode is selected by the configuration itself: as soon as a legacy "filter"
+directive is used in a proxy, the proxy works in legacy mode and the
+"filter-*" directives must not be used in the same proxy, including when they
+are inherited from a defaults section. Conversely, a legacy "filter" directive
+is rejected in a proxy inheriting filter instances from its defaults
+section. Filters implicitly declared by other directives (for instance by the
+"compression" options, the "cache-store" rules or "use-fcgi-app") do not
+select a mode: a proxy using only such filters works in the new mode.
+The "filter-api" global option changes this last rule: when it is set to
+"downgrade-to-legacy", the proxies using only implicitly declared filters work
+in the legacy mode, so the filters not supporting the new mode keep
+working. Explicit filter declarations always win, in both modes.
+
+All the filters documented below support the new mode. The <class> name to use
+with "filter-config" may differ from the legacy filter name; it is given in
+the documentation of each filter. The "filter-config" syntax is documented
+below the legacy one.
+
+The filter instances of a proxy may be inspected with the "show filter
+classes" and "show filter instances" CLI commands (see the management
+manual).
+
+See also : "filter", "filter-config", "filter-enable", "filter-disable" and
+           "filter-sequence".
 
 9.1. Trace
 ----------
 
-filter trace [name <name>] [random-forwarding] [max-fwd <max>] [hexdump]
+filter trace [name <name>] [quiet] [random-parsing] [random-forwarding]
+             [max-fwd <max>] [hexdump]
+filter-config trace id <name> [enabled] [quiet] [random-parsing]
+             [random-forwarding] [max-fwd <max>] [hexdump]
 
   Arguments:
     <name>               is an arbitrary name that will be reported in
                          messages. If no name is provided, "TRACE" is used.
+                         With "filter-config", the instance id is mandatory
+                         and is used as the name.
 
     <quiet>              inhibits trace messages.
 
+    <random-parsing>     enables the random parsing of data. By default, this
+                         filter parses all available data. With this
+                         parameter, it only parses a random amount of the
+                         available data.
+
     <random-forwarding>  enables the random forwarding of parsed data. By
                          default, this filter forwards all previously parsed
                          data. With this parameter, it only forwards a random
@@ -31277,14 +31550,17 @@ filter trace [name <name>] [random-forwarding] [max-fwd <max>] [hexdump]
 
     <hexdump>             dumps all forwarded data to the server and the client.
 
-This filter can be used as a base to develop new filters. It defines all
-callbacks and print a message on the standard error stream (stderr) with useful
-information for all of them. It may be useful to debug the activity of other
-filters or, quite simply, HAProxy's activity.
+  This filter can be used as a base to develop new filters. It defines all
+  callbacks and prints a message on the standard error stream (stderr) with
+  useful information for all of them. It may be useful to debug the activity
+  of other filters or, quite simply, HAProxy's activity.
 
-Using <random-parsing> and/or <random-forwarding> parameters is a good way to
-tests the behavior of a filter that parses data exchanged between a client and
-a server by adding some latencies in the processing.
+  Using <random-parsing> and/or <random-forwarding> parameters is a good way
+  to test the behavior of a filter that parses data exchanged between a
+  client and a server by adding some latencies in the processing.
+
+  With the new mode, the filter class is "trace". Several instances may be
+  declared in the same proxy, each one with its own id.
 
 
 9.2. HTTP compression
@@ -31316,6 +31592,21 @@ filter other than the cache or the fcgi-app is used for the same
 listener/frontend/backend. This is important to know the filters evaluation
 order.
 
+filter-config http-comp-req
+filter-config http-comp-res
+  Explicitly declare the request or the response compression filter with the
+  new mode. These classes do not accept an id. Like their legacy counterparts
+  "filter comp-req" and "filter comp-res", they are configured with the
+  "compression" directives. Declaring "filter-config http-comp-req"
+  implicitly sets "compression direction request" and declaring
+  "filter-config http-comp-res" implicitly sets "compression direction
+  response".
+
+  Note that the legacy mode rules above about implicit declarations do not
+  apply in the new mode. The evaluation order of these filters relative to the
+  other ones is defined by the class placement and can be adjusted with the
+  "filter-sequence" directive.
+
 See also : "compression", section 9.4 about the cache filter and section 9.5
            about the fcgi-app filter.
 
@@ -31337,6 +31628,17 @@ streams. The filter runs during the stream forwarding phase, thus it will not
 be possible to perform TCP/HTTP rules on the decompressed stream (ie: res.body
 won't work).
 
+filter-config decomp-req
+  Explicitly declare the request decompression filter with the new mode. This
+  class does not accept an id. Like its legacy counterparts "filter
+  decomp-req", it is configured with the "decompression" directives.
+
+filter-config decomp-res
+  Explicitly declare the response decompression filter with the new mode. This
+  class does not accept an id. Like its legacy counterparts "filter
+  decomp-res", it is configured with the "decompression" directives.
+
+
 9.3. Stream Processing Offload Engine (SPOE)
 --------------------------------------------
 
@@ -31367,6 +31669,13 @@ of this "SPOE" stream. It means it is possible to retrieve variables of the
 main stream from the "SPOE" stream. See section 2.8 about variables for
 details.
 
+filter-config spoe id <id> [enabled] [engine <name>] config <file>
+  Declare a SPOE filter with the new mode. The instance id is mandatory, as
+  the class allows several instances in the same proxy, but it is only used by
+  the "filter-enable", "filter-disable" and "filter-sequence" directives.
+  The "engine" and "config" parameters are the same as for the legacy
+  syntax.
+
 For all information about the SPOE configuration and the SPOP specification, see
 "doc/SPOE.txt".
 
@@ -31389,6 +31698,14 @@ filter other than the compression or the fcgi-app is used for the same
 listener/frontend/backend. This is important to know the filters evaluation
 order.
 
+filter-config cache-store id <name> [enabled]
+  Declare a cache filter with the new mode. The instance id is mandatory and
+  is the name of the cache section, like the <name> parameter of the legacy
+  syntax. The rules above about implicit declarations only apply to the legacy
+  mode. In the new mode, a cache filter instance is implicitly created by the
+  "cache-store" and "cache-use" rules when needed, and its evaluation order
+  can be adjusted with the "filter-sequence" directive.
+
 See also : section 9.2 about the compression filter, section 9.5 about the
            fcgi-app filter and section 6 about cache.
 
@@ -31412,6 +31729,14 @@ fcgi-app when at least one filter other than the compression or the cache is
 used for the same backend. This is important to know the filters evaluation
 order.
 
+filter-config fcgi id <name> [enabled]
+  Declare a fcgi-app filter with the new mode. The instance id is mandatory
+  and is the name of the fcgi-app section, like the <name> parameter of the
+  legacy syntax. The rules above about implicit declarations only apply to the
+  legacy mode. In the new mode, a filter instance is implicitly created by
+  "use-fcgi-app" when needed, and its evaluation order can be adjusted with
+  the "filter-sequence" directive.
+
 See also: "use-fcgi-app", section 9.2 about the compression filter, section 9.4
           about the cache filter and section 10 about FastCGI application.
 
@@ -31433,6 +31758,13 @@ filter bwlim-in  <name> default-limit <size> default-period <time> [min-size <sz
 filter bwlim-out <name> default-limit <size> default-period <time> [min-size <sz>]
 filter bwlim-in  <name> limit <size> key <pattern> [table <table>] [min-size <sz>]
 filter bwlim-out <name> limit <size> key <pattern> [table <table>] [min-size <sz>]
+filter-config bwlim-in  id <name> [enabled] default-limit <size> default-period <time> [min-size <sz>]
+filter-config bwlim-out id <name> [enabled] default-limit <size> default-period <time> [min-size <sz>]
+filter-config bwlim-in  id <name> [enabled] limit <size> key <pattern> [table <table>] [min-size <sz>]
+filter-config bwlim-out id <name> [enabled] limit <size> key <pattern> [table <table>] [min-size <sz>]
+  With the new mode, the instance id is mandatory and is the filter name used
+  by the "set-bandwidth-limit" actions, like the <name> parameter of the
+  legacy syntax. The other parameters are the same.
 
   Arguments :
 
index 29d3e4c34dff48ab7536969926545c304af7640e..15cac51decaa388aa42ab3626426151e5b0c8868 100644 (file)
@@ -1,6 +1,6 @@
                    -----------------------------------------
-                          Filters Guide - version 3.4
-                          ( Last update: 2021-02-24 )
+                          Filters Guide - version 3.5
+                          ( Last update: 2026-10-06 )
                    ------------------------------------------
                           Author : Christopher Faulet
               Contact : christopher dot faulet at capflam dot org
@@ -29,14 +29,19 @@ SUMMARY
 
   1.    Filters introduction
   2.    How to use filters
+  2.1.      Legacy declarations
+  2.2.      Filter instances
   3.    How to write a new filter
   3.1.      API Overview
-  3.2.      Defining the filter name and its configuration
+  3.2.      Defining the filter class and its configuration
+  3.2.1.        The filter class parse callback
+  3.2.2.        The legacy keyword parser
   3.3.      Managing the filter lifecycle
   3.3.1.        Dealing with threads
   3.4.      Handling the streams activity
   3.5.      Analyzing the channels activity
   3.6.      Filtering the data exchanged
+  3.7.      Referencing a filter configuration from other directives
   4.    FAQ
 
 
@@ -61,39 +66,70 @@ could be useful to handle events at the connection level.
 
 In HAProxy configuration file, a filter is declared in a proxy section, except
 default. So the configuration corresponding to a filter declaration is attached
-to a specific proxy, and will be shared by all its instances. it is opaque from
-the HAProxy point of view, this is the filter responsibility to manage it. For
-each filter declaration matches a uniq configuration. Several declarations of
-the same filter in the same proxy will be handle as different filters by
-HAProxy.
-
-A filter instance is represented by a partially opaque context (or a state)
-attached to a stream and passed as arguments to callbacks. Through this context,
-filter instances are stateful. Depending the filter is declared in a frontend or
-a backend section, its instances will be created, respectively, when a stream is
-created or when a backend is selected. Their behaviors will also be
-different. Only instances of filters declared in a frontend section will be
-aware of the creation and the destruction of the stream, and will take part in
-the channels analyzing before the backend is defined.
+to a specific proxy, and will be shared by all its stream filter instances. It
+is opaque from the HAProxy point of view, this is the filter responsibility to
+manage it.
+
+Since HAProxy 3.5, two declaration modes coexist. The historical one, called
+the legacy mode, is based on the "filter" directive and is kept for backward
+compatibility. The new mode is based on filter instances declared with
+"filter-config". In this mode, a filter is described by a filter class and each
+declaration is an instance, possibly with an id. A proxy uses one mode or the
+other, never both: as soon as a legacy "filter" directive is used, the proxy
+works in legacy mode, otherwise the new mode is used. Implicitly declared
+filters do not select a mode, so a proxy using only implicit filters works in
+the new mode. The "filter-api" global option changes this last rule: when it
+is set to "downgrade-to-legacy", a proxy using only implicitly declared filters
+works in the legacy mode. It is a fallback for the filters not supporting the
+new mode, and the explicit declarations always win.
+
+A filter class defines the filter name, its placement in the request and
+response evaluation orders, and how its instances are parsed. A filter instance
+is the configuration of a filter in a proxy, shared by all the stream filter
+instances using it. The same class may be instantiated several times in a
+proxy, with different ids, if the class allows it. This is described in
+section 3.2.
+
+This document uses "filter instance" for the configuration entity, which is
+user-visible, and "stream filter instance" for the per-stream entity, which is
+attached to a stream and represented by 'struct filter' in the code.
+
+In legacy mode, the filter configurations are attached to the proxy and the
+declaration order defines the evaluation order. In the new mode, the order is
+defined by the class placement and can be refined per proxy with
+"filter-sequence". This is described in section 2.
+
+A stream filter instance is the per-stream object attached to a stream and
+passed as argument to the callbacks. It carries a partially opaque context (or
+a state) owned by the filter, making the instance stateful. Depending the
+filter is declared in a frontend or a backend section, its stream filter
+instances will be created, respectively, when a stream is created or when a
+backend is selected. Their behaviors will also be different. Only stream filter
+instances of filters declared in a frontend section will be aware of the
+creation and the destruction of the stream, and will take part in the channels
+analyzing before the backend is defined.
 
 It is important to remember the configuration of a filter is shared by all its
-instances, while the context of an instance is owned by a uniq stream.
+stream filter instances, while the context of a stream filter instance is owned
+by a single stream.
 
 Filters are designed to be chained. It is possible to declare several filters in
-the same proxy section. The declaration order is important because filters will
-be called one after the other respecting this order. Frontend and backend
-filters are also chained, frontend ones called first. Even if the filters
-processing is serialized, each filter will bahave as it was alone (unless it was
-developed to be aware of other filters). For all that, some constraints are
-imposed to filters, especially when data exchanged between the client and the
-server are processed. We will discuss again these constraints when we will tackle
-the subject of writing a filter.
+the same proxy section. The evaluation order is the declaration order in legacy
+mode, and the class order - possibly refined with "filter-sequence" - in the
+new mode. Frontend and backend filters are also chained, frontend ones called
+first. Even if the filters processing is serialized, each filter will behave
+as it was alone (unless it was developed to be aware of other filters). For
+all that, some constraints are imposed to filters, especially when data
+exchanged between the client and the server are processed. We will discuss
+again these constraints when we will tackle the subject of writing a filter.
 
 
 
 2. HOW TO USE FILTERS
 ---------------------
 
+2.1. LEGACY DECLARATIONS
+
 To use a filter, the parameter 'filter' should be used, followed by the filter
 name and, optionally, its configuration in the desired listen, frontend or
 backend section. For instance :
@@ -104,11 +140,11 @@ backend section. For instance :
         ...
 
 
-See doc/configuration.txt for a formal definition of the parameter 'filter'.
 Note that additional parameters on the filter line must be parsed by the filter
-itself.
+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 filters is reported by 'haproxy -vv' :
+The list of available filter keywords is reported by 'haproxy -vv' :
 
     $> haproxy -vv
     HAProxy version 1.7-dev2-3a1d4a-33 2016/03/21
@@ -125,13 +161,13 @@ Multiple filter lines can be used in a proxy section to chain filters. Filters
 will be called in the declaration order.
 
 Some filters can support implicit declarations in certain circumstances
-(without the filter line). This is not recommended for new features but are
+(without the filter line). This is not recommended for new features but is
 useful for existing ones moved in a filter, for backward compatibility
-reasons. Implicit declarations are supported when there is only one filter used
-on a proxy. When several filters are used, explicit declarations are mandatory.
-The HTTP compression filter is one of these filters. Alone, using 'compression'
-keywords is enough to use it. But when at least a second filter is used, a
-filter line must be added.
+reasons. In legacy mode, implicit declarations are supported when there is only
+one filter used on a proxy. When several filters are used, explicit
+declarations are mandatory. The HTTP compression filter is one of these
+filters. Alone, using 'compression' keywords is enough to use it. But when at
+least a second filter is used, a filter line must be added.
 
     # filter line is optional
     listen t1
@@ -150,6 +186,82 @@ filter line must be added.
         server srv x.x.x.x:80
 
 
+2.2. FILTER INSTANCES
+
+The new mode is based on filter instances. Each instance belongs to a
+filter class and may have an id. It is declared with the "filter-config"
+directive :
+
+    listen test
+        ...
+        filter-config trace id TST enabled
+        ...
+
+The syntax is the following one :
+
+    filter-config <class> [ id <id> ] [ enabled ] [ <arguments> ... ]
+
+  * '<class>' is the filter class name, registered by the filter (see section
+    3.2).
+
+  * '<id>' is mandatory for classes allowing several instances in the same
+    proxy. It is used to refer to the instance in the other directives.
+
+  * 'enabled' activates the instance. Without it, the instance is created
+    but disabled. It may be activated later with "filter-enable".
+
+  * '<arguments>' are the arguments of the filter class. See the filter
+    documentation for details.
+
+Several filter instances of the same class may be declared in the same proxy,
+as long as the class supports it and each filter instance has an id. Note that
+in legacy mode, each "filter" line also creates an independent configuration,
+but the filter itself is responsible for rejecting duplicates: the class
+capability and the instance ids do not exist in this mode. Filter instances are
+also inherited from the defaults section: an instance declared locally with the
+same class and id replaces the inherited one, and an explicit instance replaces
+an implicitly declared one.
+
+The filter instances of a proxy may be enabled or disabled at any time with :
+
+    filter-enable  <class>[/<id>] [<class>[/<id>] ...]
+    filter-disable <class>[/<id>] [<class>[/<id>] ...]
+
+Without an id, all the instances of the class are updated.
+
+By default, filters are evaluated following the class order (see section 3.2).
+The order may be changed, per proxy and independently for the request and
+response channels, with the "filter-sequence" directive :
+
+    filter-sequence {global|request|response} <instance>:<pos>(<entity>) [<instance>:<pos>(<entity>) ...]
+
+where '<instance>' is the instance to move (class[/id]), '<pos>' is "before" or
+"after" and '<entity>' is a class or an instance (class[/id]) used as the
+reference. "A:before(B)" means the instance A is evaluated before the entity
+B, "A:after(B)" means A is evaluated after B. For instance, to evaluate the
+"cache-store" instance with the id "content" before the "spoe" class :
+
+    filter-sequence response cache-store/content:before(spoe)
+
+The "global" order only drives the side-independent operations (configuration
+checks, initialization and destruction). Moving an instance in this order does
+not change its request or response positions.
+
+The filter instances of a proxy and their evaluation orders may be inspected at
+runtime with the CLI :
+
+    show filter classes [global|request|response]
+    show filter instances <px> [global|request|response]
+    show filter instance <px> <class>[/<id>]
+
+A filter may also be implicitly declared by another directive, for instance by
+the options of the feature it implements. In the new mode, use
+flt_add_implicit_instance() from the directive parser (see section 3.2.1):
+it creates an enabled instance, with an optional id, that an explicit
+"filter-config" declaration may replace. Unlike the legacy mode, an implicit
+declaration may coexist with other filters in the same proxy.
+
+
 
 
 3. HOW TO WRITE A NEW FILTER
@@ -174,7 +286,7 @@ To ease the filters integration, it is better to follow some conventions :
 The filter 'trace' can be used as a template to write new filter. It is a good
 start to see how filters really work.
 
-3.1 API OVERVIEW
+3.1. API OVERVIEW
 ----------------
 
 Writing a filter can be summarized to write functions and attach them to the
@@ -243,118 +355,204 @@ existing callbacks. Available callbacks are listed in the following structure :
 We will explain in following parts when these callbacks are called and what they
 should do.
 
-Filters are declared in proxy sections. So each proxy have an ordered list of
-filters, possibly empty if no filter is used. When the configuration of a proxy
-is parsed, each filter line represents an entry in this list. In the structure
-'proxy', the filters configurations are stored in the field 'filter_configs',
-each one of type 'struct flt_conf *' :
+Filters are declared in proxy sections. So each proxy has an ordered list of
+filters, possibly empty if no filter is used. In legacy mode, each "filter"
+line represents an entry in this list: the filter configurations are stored in
+the field 'filter_configs' of the 'proxy' structure, each one of type
+'struct flt_conf *'. In the new mode, the filter instances are stored
+separately and the core builds the evaluation orders from them, but a filter
+still only deals with 'struct flt_conf' once its filter instance is finalized.
 
     /*
      * Structure representing the filter configuration, attached to a proxy and
      * accessible from a filter when instantiated in a stream
      */
     struct flt_conf {
-        const char     *id;   /* The filter id */
-        struct flt_ops *ops;  /* The filter callbacks */
-        void           *conf; /* The filter configuration */
-        struct list     list; /* Next filter for the same proxy */
-        unsigned int    flags; /* FLT_CFG_FL_* */
+        const char     *name;   /* The filter name */
+        const char     *id;     /* The filter id */
+        struct flt_ops *ops;    /* The filter callbacks */
+        void           *conf;   /* The filter configuration */
+        unsigned int    flags;  /* FLT_CFG_FL_* */
+        struct list     list;   /* Next filter for the same proxy */
+        struct list     by_req; /* Internal link in the request order */
+        struct list     by_res; /* Internal link in the response order */
     };
 
-  * 'flt_conf.id' is an identifier, defined by the filter. It can be
-    NULL. HAProxy does not use this field. Filters can use it in log messages or
-    as a uniq identifier to check multiple declarations. It is the filter
-    responsibility to free it, if necessary.
+  * 'flt_conf.name' is the filter class name of the configuration. In legacy
+    mode, it may be set by the parsing function. It is not used by HAProxy.
+
+  * 'flt_conf.id' is an identifier, defined by the filter. It can be NULL. It
+    is used by the core to look a filter configuration up by its id, with
+    flt_find_conf() (see section 3.7), so in the new mode the class parse
+    callback must set it like the legacy parsing function does. Filters can
+    also use it in log messages or as a uniq identifier to check multiple
+    declarations. HAProxy never frees or duplicates it: it is the filter
+    responsibility to free it, if necessary, once the configuration is
+    released.
 
   * 'flt_conf.conf' is opaque. It is the internal configuration of a filter,
-    generally allocated and filled by its parsing function (See Â§ 3.2). It is
-    the filter responsibility to free it.
+    generally allocated and filled by its parsing function (see sections 3.2.1
+    and 3.2.2). It is the filter responsibility to free it.
 
   * 'flt_conf.ops' references the callbacks implemented by the filter. This
-    field must be set during the parsing phase (See Â§ 3.2) and can be refine
-    during the initialization phase (See Â§ 3.3). If it is dynamically allocated,
-    it is the filter responsibility to free it.
+    field must be set during the parsing phase (see section 3.2) and can be
+    refined during the initialization phase (see section 3.3). If it is
+    dynamically allocated, it is the filter responsibility to free it. In the
+    new mode, it is mandatory: an instance whose class parse callback does not
+    set it is rejected at startup.
 
   * 'flt_conf.flags' is a bitfield to specify the filter capabilities. For now,
     only FLT_CFG_FL_HTX may be set when a filter is able to process HTX
     streams. If not set, the filter is excluded from the HTTP filtering.
 
+  * 'flt_conf.list', 'flt_conf.by_req' and 'flt_conf.by_res' are internal
+    links, managed by the core. Filters must not use them.
 
-The filter configuration is global and shared by all its instances. A filter
-instance is created in the context of a stream and attached to this stream. in
-the structure 'stream', the field 'strm_flt' is the state of all filter
-instances attached to a stream :
+
+The filter configuration is global and shared by all its stream filter
+instances. A stream filter instance is created in the context of a stream and
+attached to this stream. In the structure 'stream', the field 'strm_flt' is the
+state of all stream filter instances attached to a stream. The per-channel
+state is stored in the 'flt' field of each 'channel' (a 'struct chn_flt') :
 
     /*
      * Structure representing the "global" state of filters attached to a
      * stream.
      */
     struct strm_flt {
-        struct list    filters;             /* List of filters attached to a stream */
-        struct filter *current[2];          /* From which filter resume processing, for a specific channel.
-                                             * This is used for resumable callbacks only,
-                                             * If NULL, we start from the first filter.
-                                             * 0: request channel, 1: response channel */
-        unsigned short flags;               /* STRM_FL_* */
-        unsigned char  nb_req_data_filters; /* Number of data filters registered on the request channel */
-        unsigned char  nb_rsp_data_filters; /* Number of data filters registered on the response channel */
-        unsigned long long offset[2];       /* gloal offset of input data already filtered for a specific channel
-                                             * 0: request channel, 1: response channel */
+        struct list    filters;               /* List of filters attached to a stream */
+        unsigned short flags;                 /* STRM_FL_* */
+    };
+
+    /* structure holding filter state for some members that are channel oriented */
+    struct chn_flt {
+        struct list filters;                  /* List of filters attached to a channel */
+        struct filter *current;               /* From which filter resume processing, for a specific channel. */
+        unsigned char nb_data_filters;        /* Number of data filters registered on channel */
+        unsigned long long offset;
     };
 
 
-Filter instances attached to a stream are stored in the field
-'strm_flt.filters', each instance is of type 'struct filter *' :
+Stream filter instances attached to a stream are stored in the field
+'strm_flt.filters', each one is of type 'struct filter *'. The same stream
+filter instance is linked in the request and/or the response filter list of
+the channels, depending on the sides supported by its class or instance:
 
     /*
-     * Structure representing a filter instance attached to a stream
-     *
-     * 2D-Array fields are used to store info per channel. The first index
-     * stands for the request channel, and the second one for the response
-     * channel.  Especially, <next> and <fwd> are offsets representing amount of
-     * data that the filter are, respectively, parsed and forwarded on a
-     * channel. Filters can access these values using FLT_NXT and FLT_FWD
-     * macros.
+     * Structure representing a stream filter instance attached to a stream
      */
     struct filter {
-        struct flt_conf *config; /* the filter's configuration */
-        void           *ctx;     /* The filter context (opaque) */
-        unsigned short  flags;   /* FLT_FL_* */
-        unsigned long long offset[2];   /* Offset of input data already filtered for a specific channel
-                                         * 0: request channel, 1: response channel */
-        unsigned int    pre_analyzers;  /* bit field indicating analyzers to
-                                         * pre-process */
-        unsigned int    post_analyzers; /* bit field indicating analyzers to
-                                         * post-process */
-        struct list     list;    /* Next filter for the same proxy/stream */
+        struct flt_conf *config;           /* the filter's configuration */
+        void           *ctx;               /* The filter context (opaque) */
+        unsigned short  flags;             /* FLT_FL_* */
+        unsigned int    calls;             /* number of calls */
+        unsigned long long offset[2];      /* Offset of input data already filtered for a specific channel
+                                            * 0: request channel, 1: response channel */
+        unsigned int    pre_analyzers;     /* bit field indicating analyzers to pre-process */
+        unsigned int    post_analyzers;    /* bit field indicating analyzers to post-process */
+        struct list     list;              /* Filter list for the stream */
+        struct list     req_list;          /* Filter list for request channel */
+        struct list     res_list;          /* Filter list for response channel */
     };
 
-  * 'filter.config' is the filter configuration previously described. All
-    instances of a filter share it.
+  * 'filter.config' is the filter configuration previously described. All the
+    stream filter instances of a proxy share it. In the new mode, a filter
+    instance may be instantiated on the request channel, the response channel,
+    or both, following the class placement and the instance state.
 
   * 'filter.ctx' is an opaque context. It is managed by the filter, so it is its
     responsibility to free it.
 
-  * 'filter.pre_analyzers and 'filter.post_analyzers will be described later
-    (See Â§ 3.5).
+  * 'filter.pre_analyzers' and 'filter.post_analyzers' will be described later
+    (see section 3.5).
 
-  * 'filter.offset' will be described later (See Â§ 3.6).
+  * 'filter.offset' will be described later (see section 3.6).
 
 
-3.2. DEFINING THE FILTER NAME AND ITS CONFIGURATION
----------------------------------------------------
+3.2. DEFINING THE FILTER CLASS AND ITS CONFIGURATION
+-----------------------------------------------------
 
-During the filter development, the first thing to do is to add it in the
-supported filters. To do so, its name must be registered as a valid keyword on
-the filter line :
+To make a filter usable in the new mode, its class must be declared, registered
+and placed, and a parse callback must be provided. To keep the legacy mode
+working, a keyword parser must also be registered (see section 3.2.2).
 
-    /* Declare the filter parser for "my_filter" keyword */
-    static struct flt_kw_list flt_kws = { "MY_FILTER_SCOPE", { }, {
-            { "my_filter", parse_my_filter_cfg, NULL /* private data */ },
-            { NULL, NULL, NULL },
-        }
+The filter class is described by the following structure :
+
+    struct filter_class {
+        const char *name;           /* The filter class name, must be uniq */
+        unsigned int flags;         /* FLT_CLS_FL_* */
+
+        /* Callback to parse the arguments of a filter instance and produce
+         * the filter configuration (inst->fconf).
+         */
+        int (*parse)(char **args, struct proxy *px,
+                     struct filter_instance *inst, char **err);
+
+        struct {
+            struct list before; /* classes moved before current class for request */
+            struct list after;  /* classes moved after current class for request */
+            struct list list;   /* classes registered on request analysis */
+        } req;
+        struct {
+            struct list before; /* classes moved before current class on response */
+            struct list after;  /* classes moved after current class on response */
+            struct list list;   /* classes registered on response analysis */
+        } res;
+
+        struct list list; /* Link in global list of filter classes */
     };
-    INITCALL1(STG_REGISTER, flt_register_keywords, &flt_kws);
+
+  * 'filter_class.name' is the name used by the "filter-config" directive and
+    the other "filter-*" directives.
+
+  * 'filter_class.flags' is a bitfield to specify the class capabilities. For
+    now, only FLT_CLS_FL_MULTI may be set when several instances of the class
+    are allowed in the same proxy. In this case, an id is mandatory for each
+    instance. Otherwise a second instance of the class in the same proxy is
+    rejected, even with a different id.
+
+  * 'filter_class.parse' is the callback parsing a filter instance (see
+    section 3.2.1). It may be NULL, but in this case the class cannot be used
+    with "filter-config" at all.
+
+  * 'filter_class.req' and 'filter_class.res' anchor the class in the request
+    and response class orders (see below).
+
+  * 'filter_class.list' links the class in the global registry.
+
+The class is registered with filter_register_class(). It must then be placed,
+on each side it supports, relative to an already placed class, with
+filter_place_class() :
+
+    static struct filter_class flt_my_filter_cls;
+
+    static int my_filter_register_class(void)
+    {
+        if (filter_register_class(&flt_my_filter_cls, "my-filter", my_filter_parse_instance) < 0)
+            return -1;
+
+        /* Evaluate the filter before SPOE on the request path and after the Lua
+         * filters on the response path. */
+        if (filter_place_class(&flt_my_filter_cls, FLT_SIDE_REQ, "spoe", FLT_POS_BEFORE) < 0)
+            return -1;
+        if (filter_place_class(&flt_my_filter_cls, FLT_SIDE_RES, "lua", FLT_POS_AFTER) < 0)
+            return -1;
+        return 0;
+    }
+    INITCALL1(STG_INIT, my_filter_register_class);
+
+filter_register_class_full() is a helper for the common case where the class is
+placed on both sides at the same position relative to the same class.
+
+A class placed on neither side is rejected at startup because it would be
+useless. A class may be placed on both sides with different positions. The
+placement defines the default evaluation order of the instances of the class
+relative to the other classes. The global order follows the class registration
+order.
+
+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.
 
 
 Then the filter internal configuration must be defined. For instance :
@@ -378,7 +576,136 @@ variable is used :
      };
 
 
-Finally, the function to parse the filter configuration must be written, here
+3.2.1. THE FILTER CLASS PARSE CALLBACK
+
+The class parse callback is called once per filter instance, after the whole
+configuration was parsed, to produce the filter configuration attached to the
+filter instance ('inst->fconf'). 'inst->conf.argv' contains the arguments of
+the instance, i.e. the "filter-config" arguments following the class name and
+the optional "id <id>" and "enabled" tokens. They are NULL-terminated :
+
+    /* Return 0 on success, else -1 with <err> filled */
+    static int
+    my_filter_parse_instance(char **args, struct proxy *px,
+                             struct filter_instance *inst, char **err)
+    {
+        struct my_filter_config *my_conf;
+
+        /* All the arguments must be consumed by the callback */
+        if (*args) {
+            memprintf(err, "'%s' : unexpected argument '%s'",
+                      inst->class->name, args[0]);
+            return -1;
+        }
+
+        /* Allocate the internal configuration used by the filter */
+        my_conf = calloc(1, sizeof(*my_conf));
+        if (!my_conf) {
+            memprintf(err, "out of memory");
+            return -1;
+        }
+        my_conf->proxy = px;
+
+        /* ... */
+
+        /* Set the filter id and the callbacks. inst->fconf was allocated by
+         * HAProxy and is never NULL here. */
+        inst->fconf->id   = my_filter_flt_id;
+        inst->fconf->ops  = &my_filter_ops;
+
+        /* Last, save the internal configuration */
+        inst->fconf->conf = my_conf;
+
+        /* If the filter supports HTX streams */
+        inst->fconf->flags |= FLT_CFG_FL_HTX;
+        return 0;
+    }
+
+The filter instance is passed to the callback and is the only chance to read
+its attributes: its id ('inst->id'), its flags ('inst->flags') and the class it
+belongs to ('inst->class'). The following flags may be set :
+
+  * 'FLT_INST_F_IMPLICIT' : the instance was created implicitly by another
+    directive with flt_add_implicit_instance() (see below).
+
+  * 'FLT_INST_F_INHERITED' : the instance was inherited from the defaults
+    section of the proxy.
+
+The constraints below must be kept in mind while writing the callback :
+
+  * 'inst->fconf->ops' must be set, otherwise the configuration is rejected at
+    startup. All the arguments of the instance must be consumed.
+
+  * 'inst->fconf->conf' is owned by the filter. It is released by the filter,
+    generally in the 'flt_ops.deinit' callback (see section 3.3).
+
+  * The filter instance is a shared, read-only object once the startup is
+    completed: it is used to instantiate the filter for all the streams of the
+    proxy. Never store per-stream data in the filter instance or in its
+    configuration. Use 'filter.ctx' for that purpose.
+
+  * The callback is called after the configuration was fully parsed and after
+    the filter instances of the defaults section were inherited. It is not
+    called while the "filter-config" directive is read. As a consequence, other
+    directives must not keep pointers to filter instances during the parsing:
+    they must resolve the filter configuration at the check stage, using
+    flt_find_conf() and flt_foreach_conf() (see section 3.7).
+
+  * Side effects on the proxy options (for instance enabling a channel
+    direction) are allowed but must be idempotent, because the callback is also
+    called for inherited and implicit filter instances.
+
+  * For implicit filter instances (FLT_INST_F_IMPLICIT), the callback must not
+    re-enable an option that the directive did not enable, and must not add
+    side effects: it only finalizes the filter configuration. The filter is
+    already enabled by the directive and an explicit declaration wins over an
+    implicit one.
+
+  * For inherited filter instances (FLT_INST_F_INHERITED), the callback is
+    called again in the child proxy. Capability-dependent fields must be left
+    unset if the final proxy does not support them. In this case, the instance
+    is kept as metadata but the filter can simply decline its attachment to the
+    streams by returning 0 in 'flt_ops.attach'.
+
+  * A filter instance is instantiated on a stream only if it is enabled, in
+    which case a stream filter instance is created. It is enabled by the
+    "enabled" token, by the directive that created it implicitly or by
+    "filter-enable". Otherwise it is disabled by default.
+
+  * The id and the class name may only contain alphanumeric characters, '.',
+    '_' and '-'. In the configuration and the CLI, a filter instance is
+    referred to as <class>/<id>.
+
+Filters that are enabled by another directive (the compression options, the
+cache rules, ...) should create their filter instance with
+flt_add_implicit_instance(). It may be called during the configuration
+parsing, from the directive that enables the filter :
+
+    flt_add_implicit_instance(px, cls, "my-id", args, file, line);
+
+where 'cls' is the filter class, 'args' the arguments of the filter instance
+(or NULL), and 'file'/'line' the location of the directive. The instance is
+created enabled and marked FLT_INST_F_IMPLICIT.
+
+
+3.2.2. THE LEGACY KEYWORD PARSER
+
+To keep the support of the legacy "filter" directive, the filter must register
+its name as a valid keyword on the filter line. This is the historical way to
+declare a filter and it is optional: a filter supporting only the new mode
+cannot be used with the "filter" directive, and no other directive may
+transitively enable it in legacy mode either.
+
+    /* Declare the filter parser for "my_filter" keyword */
+    static struct flt_kw_list flt_kws = { "MY_FILTER_SCOPE", { }, {
+            { "my_filter", parse_my_filter_cfg, NULL /* private data */ },
+            { NULL, NULL, NULL },
+        }
+    };
+    INITCALL1(STG_REGISTER, flt_register_keywords, &flt_kws);
+
+
+The function to parse the filter configuration must be written, here
 'parse_my_filter_cfg'. This function must parse all remaining keywords on the
 filter line :
 
@@ -440,14 +767,38 @@ filter line :
 WARNING : In this parsing function, 'flt_conf->ops' must be initialized. All
           arguments of the filter line must also be parsed. This is mandatory.
 
-In the previous example, the filter lne should be read as follows :
+In the previous example, the filter line should be read as follows :
 
     filter my_filter name MY_NAME ...
 
+A filter supporting both modes should not duplicate the parsing code. The class
+parse callback may delegate the instance arguments to the legacy keyword
+parser with flt_parse_instance_legacy() :
+
+    /* Return 0 on success, else -1 with <err> filled */
+    static int
+    my_filter_parse_instance(char **args, struct proxy *px,
+                             struct filter_instance *inst, char **err)
+    {
+        return flt_parse_instance_legacy(px, inst, err, "my_filter",
+                                         parse_my_filter_cfg, NULL);
+    }
+
+This helper rebuilds the argument vector expected by the legacy parser (the
+keyword followed by the arguments, terminated by an empty string) from
+'inst->conf.argv' and checks that all the arguments were consumed.
+
+WARNING : The legacy parser may rely on parsing-time globals, the most common
+          one being 'curproxy'. Such globals are not set when the class parse
+          callback is called (after the configuration parsing), so they must be
+          saved and restored around the call by the filter if needed.
+
 
-Optionally, by implementing the 'flt_ops.check' callback, an extra set is added
-to check the internal configuration of the filter after the parsing phase, when
-the HAProxy configuration is fully defined. For instance :
+Optionally, by implementing the 'flt_ops.check' callback, an extra check is
+added for the internal configuration of the filter after the parsing phase,
+when the HAProxy configuration is fully defined. It is called once per proxy,
+for all the finalized filter instances in the new mode, or for all the legacy
+configurations otherwise. For instance :
 
     /* Check configuration of a trace filter for a specified proxy.
      * Return 1 on error, else 0. */
@@ -455,7 +806,7 @@ the HAProxy configuration is fully defined. For instance :
     my_filter_config_check(struct proxy *px, struct flt_conf *my_conf)
     {
         if (px->mode != PR_MODE_HTTP) {
-            Alert("The filter 'my_filter' cannot be used in non-HTTP mode.\n");
+            ha_alert("The filter 'my_filter' cannot be used in non-HTTP mode.\n");
             return 1;
         }
 
@@ -469,8 +820,8 @@ the HAProxy configuration is fully defined. For instance :
 3.3. MANAGING THE FILTER LIFECYCLE
 ----------------------------------
 
-Once the configuration parsed and checked, filters are ready to by used. There
-are two main callbacks to manage the filter lifecycle :
+Once the configuration is parsed and checked, filters are ready to be used.
+There are two main callbacks to manage the filter lifecycle :
 
   * 'flt_ops.init' : It initializes the filter for a proxy. This callback may be
                      defined to finish the filter configuration.
@@ -479,6 +830,12 @@ are two main callbacks to manage the filter lifecycle :
                        callback have done. This callback is useful to release
                        memory allocated for the filter configuration.
 
+In the new mode, these callbacks are called once per filter instance, in the
+global order, for all the filter instances of the proxy - including the
+disabled ones. The 'enabled' state only controls whether a filter instance is
+instantiated on streams. The filter configuration ('flt_conf') is owned by the
+filter instance and is released with it when the proxy is destroyed.
+
 Here is an example :
 
     /* Initialize the filter. Returns -1 on error, else 0. */
@@ -507,7 +864,7 @@ Here is an example :
     }
 
 
-3.3.1 DEALING WITH THREADS
+3.3.1. DEALING WITH THREADS
 --------------------------
 
 When HAProxy is compiled with the threads support and started with more that one
@@ -590,26 +947,26 @@ For instance :
 WARNING : Handling the streams creation and destruction is only possible for
           filters defined on proxies with the frontend capability.
 
-In addition, it is possible to handle creation and destruction of filter
+In addition, it is possible to handle creation and destruction of stream filter
 instances using following callbacks:
 
-  * 'flt_ops.attach' : It is called after a filter instance creation, when it is
-                       attached to a stream. This happens when the stream is
-                       started for filters defined on the stream's frontend and
-                       when the backend is set for filters declared on the
-                       stream's backend. It is possible to ignore the filter, if
-                       needed, by returning 0. This could be useful to have
-                       conditional filtering.
+  * 'flt_ops.attach' : It is called after a stream filter instance creation,
+                       when it is attached to a stream. This happens when the
+                       stream is started for filters defined on the stream's
+                       frontend and when the backend is set for filters
+                       declared on the stream's backend. It is possible to
+                       ignore the filter, if needed, by returning 0. This
+                       could be useful to have conditional filtering.
 
-  * 'flt_ops.detach' : It is called when a filter instance is detached from a
-                       stream, before its destruction. This happens when the
-                       stream is stopped for filters defined on the stream's
-                       frontend and when the analyze ends for filters defined on
-                       the stream's backend.
+  * 'flt_ops.detach' : It is called when a stream filter instance is detached
+                       from a stream, before its destruction. This happens when
+                       the stream is stopped for filters defined on the
+                       stream's frontend and when the analyze ends for filters
+                       defined on the stream's backend.
 
 For instance :
 
-    /* Called when a filter instance is created and attach to a stream */
+    /* Called when a stream filter instance is created and attached to a stream */
     static int
     my_filter_attach(struct stream *s, struct filter *filter)
     {
@@ -620,8 +977,8 @@ For instance :
         return 1;
     }
 
-    /* Called when a filter instance is detach from a stream, just before its
-     * destruction */
+    /* Called when a stream filter instance is detached from a stream, just
+     * before its destruction */
     static void
     my_filter_detach(struct stream *s, struct filter *filter)
     {
@@ -923,7 +1280,7 @@ By zooming on an analyzer box we have:
                      ...
 
 
- 3.6. FILTERING THE DATA EXCHANGED
+3.6. FILTERING THE DATA EXCHANGED
 -----------------------------------
 
 WARNING : To fully understand this part, it is important to be aware on how the
@@ -1000,7 +1357,7 @@ filtered stream and 'chn' is the considered channel. However, there is no reason
 for a filter to use these macros or take care of these offsets.
 
 
-3.6.1 FILTERING DATA ON TCP STREAMS
+3.6.1. FILTERING DATA ON TCP STREAMS
 -----------------------------------
 
 The TCP data filtering for TCP streams is the easy case, because HAProxy do not
@@ -1045,7 +1402,7 @@ set. At any time, the IS_HTX_STRM() macros may be used on the stream to know if
 it is an HTX stream or a TCP stream.
 
 
-3.6.2 FILTERING DATA ON HTTP STREAMS
+3.6.2. FILTERING DATA ON HTTP STREAMS
 ------------------------------------
 
 The HTTP data filtering is a bit more complex because HAProxy data are
@@ -1171,7 +1528,7 @@ Then, to finish, there are 2 informational callbacks :
     occurs.
 
 
-3.6.3 REWRITING DATA
+3.6.3. REWRITING DATA
 --------------------
 
 The last part, and the trickiest one about the data filtering, is about the data
@@ -1186,3 +1543,100 @@ update data itself, i.e. the buffer offsets, using following function :
     the size of incoming data leads to an undefined behavior.
 
 A good example of filter changing the data size is the HTTP compression filter.
+
+
+3.7. REFERENCING A FILTER CONFIGURATION FROM OTHER DIRECTIVES
+-------------------------------------------------------------
+
+A directive (a rule, an action, ...) may need to reference a filter, for
+instance to share its configuration. It must not store a pointer to the filter
+configuration during the parsing: in the new mode, the filter instances are
+finalized during the post-parsing stage, after the rule was parsed, and they
+may be inherited or replaced. References must be resolved later, from the check
+callback of the rule ('act_rule.check_ptr'), which is called once the filter
+instances were finalized.
+
+The following helpers work in both modes and must be used for that purpose :
+
+  * 'flt_has_filter(px)' returns true if the proxy uses at least one filter, in
+    either mode. It replaces tests of 'px->filter_configs'.
+
+  * 'flt_foreach_conf(px, side, fct, data)' iterates over the filter
+    configurations of the proxy, in the evaluation order of the given side
+    (FLT_SIDE_GLOBAL, FLT_SIDE_REQ or FLT_SIDE_RES). The '<fct>' callback is
+    called with each 'struct flt_conf *' and must return 0 to continue. In
+    legacy mode, the declaration order is used.
+
+  * 'flt_find_conf(px, flt_id, side, match, data)' returns the first filter
+    configuration matching the id '<flt_id>' (any id if NULL) and the optional
+    '<match>' callback. This is the recommended way to find the filter
+    configuration referenced by a rule.
+
+  * 'flt_find_instance(px, cls, id)' returns the first filter instance of the
+    class '<cls>' matching the id (any id if NULL), or NULL. It may be used
+    when the filter instance itself is needed, but not to keep a pointer to it.
+
+The match callback passed to flt_find_conf() typically compares the filter
+configuration ('fconf->conf') with the reference stored in the rule. Since the
+helpers select the right source according to the proxy mode, a filter
+supporting both modes must not scan 'px->filter_configs' directly.
+
+
+4. FAQ
+------
+
+4.1. Which mode should a new filter support?
+
+    A new filter may support only the new mode. The legacy "filter" directive
+    has to be supported only to remain compatible with existing configurations
+    relying on it. That said, if the filter results from the conversion of an
+    existing feature, supporting both modes is recommended. Both modes may
+    share the same parsing code through flt_parse_instance_legacy() (see section
+    3.2.2).
+
+    A filter is usable in the new mode as soon as it registers a filter class
+    (section 3.2); it is usable in the legacy mode as soon as it registers a
+    "filter" keyword (section 3.2.2). Nothing forces a filter to support both,
+    but a filter supporting only the new mode cannot be enabled by another
+    directive in legacy mode.
+
+4.2. Why is my filter instance ignored?
+
+    By default, a filter instance declared with "filter-config" is disabled.
+    The "enabled" token or a "filter-enable" directive is required to activate
+    it. The state of a filter instance is reported by "show filter instance"
+    (section 2.2).
+
+4.3. When is the configuration of my filter parsed?
+
+    In the new mode, the class parse callback is called after the whole
+    configuration was parsed, once per filter instance, and after the filter
+    instances of the defaults section were inherited. It is not called while
+    the "filter-config" line is read. Directives that reference the filter must
+    therefore resolve it at the check stage with flt_find_conf() (section 3.7).
+    On the contrary, in legacy mode the parsing function is called while the
+    "filter" line is read.
+
+4.4. How can I declare several filter instances of the same class in a proxy?
+
+    The class must set FLT_CLS_FL_MULTI, in which case each filter instance must
+    have an id. Otherwise a single filter instance per proxy is allowed, and a
+    second one - explicit, inherited or implicit - is rejected as a duplicate.
+
+4.5. How is the evaluation order determined?
+
+    In the new mode, by the class placement on the request and response sides
+    (section 3.2) and, possibly, by the "filter-sequence" directive on a per
+    proxy basis (section 2.2). The global order follows the class registration
+    order. A filter must not assume a fixed order relative to other filters
+    unless it owns the corresponding placements or sequences.
+
+    In legacy mode, the declaration order of the "filter" directives is used.
+
+4.6. Can a filter instance be shared between proxies?
+
+    No. The filter instances of a defaults section are copied into the
+    inheriting proxies, each one with its own filter configuration. A filter
+    configuration, and a fortiori a stream filter instance context, must never
+    be shared between proxies, streams or threads unless the filter explicitly
+    supports it.
index c314537df3e2f9ff465c3cfdc21de23ff720360e..5d0a7322dbf4597759e88c95e6e9af84b3ed51cf 100644 (file)
@@ -3274,6 +3274,38 @@ show fd [-!plcfbs]* [[<tgid>]/[<fd>] | <fd>]
   case the output line will be suffixed with an exclamation mark ('!'). This
   may help find a starting point when trying to diagnose an incident.
 
+show filter classes [global|request|response]
+  Dump the list of the registered filter classes in the requested evaluation
+  order. Without argument, the global, request and response orders are dumped,
+  one per line. The classes are separated by ">" characters and are dumped in
+  their evaluation order. The global order is used for the side-independent
+  filter operations (configuration checks, initialization and destruction),
+  while the request and response orders define the order in which the filters
+  are evaluated on the corresponding channel. See section 9 of the
+  configuration manual for details about the filter classes and their
+  declaration modes.
+
+show filter instance <px> <class>[/<id>]
+  Dump the configuration of the filter instance <class>[/<id>] attached to the
+  proxy <px>, which may be a regular proxy or a named defaults section. The
+  output reports the filter class, the instance id, its state (enabled or
+  disabled, implicit or inherited), the sides its class is placed on, the
+  location of its declaration and its raw arguments. An error is reported if
+  the instance does not exist, or if no id is given while several instances of
+  the class exist in the proxy. Only filter instances are reported; filters
+  declared with the legacy "filter" directive are not listed.
+
+show filter instances <px> [global|request|response]
+  Dump the filter instances attached to the proxy <px> in the requested
+  evaluation order. Without argument, one line is dumped for each of the
+  global, request and response orders. The instances are separated by ">"
+  characters and the "<none>" string is printed for an empty order. Only
+  filter instances are reported: in legacy mode, the filters declared with the
+  "filter" directive are not listed. Only filters implicitly declared by other
+  directives, which are mirrored as filter instances, may then appear. See
+  section 9 of the configuration manual for details about the filters
+  declaration modes.
+
 show info [typed|json] [desc] [float]
   Dump info about haproxy status on current process. If "typed" is passed as an
   optional argument, field numbers, names and types are emitted as well so that