]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: cache: Require an explicit filter declaration if other filters are used
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 11 Dec 2018 08:18:27 +0000 (09:18 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 11 Dec 2018 16:09:31 +0000 (17:09 +0100)
As for the compression filter, the cache filter must be explicitly declared
(using the filter keyword) if other filters than cache are used. It is mandatory
to explicitly define the filters order.

Documentation has been updated accordingly.

doc/configuration.txt
src/cache.c

index 18fcf9604094c5854aab2fa52cc8d53193c5f6e7..400ec6402c8801dc6c7b44832c25ded15f2b1c9c 100644 (file)
@@ -107,6 +107,7 @@ Summary
 9.1.      Trace
 9.2.      HTTP compression
 9.3.      Stream Processing Offload Engine (SPOE)
+9.4.      Cache
 
 10.   Cache
 10.1. Limitation
@@ -17752,6 +17753,25 @@ Important note:
     The SPOE filter is highly experimental for now and was not heavily
     tested. It is really not production ready. So use it carefully.
 
+9.4. Cache
+----------
+
+filter cache <name>
+
+  Arguments :
+
+    <name>      is name of the cache section this filter will use.
+
+The cache uses a filter to store cacheable responses. The HTTP rules
+"cache-store" and "cache-use" must be used to define how and when to use a
+cache. By default the correpsonding filter is implicitly defined. And when no
+other filter than cache is used, it is enough. But it is mandatory to
+explicitly use a filter line to use a cache when two or more filters are used
+for the same listener/frontend/backend. This is important to know the filters
+evaluation order.
+
+See also : section 10 about cache.
+
 10. Cache
 ---------
 
@@ -17789,9 +17809,11 @@ The cache won't store and won't deliver objects in these cases:
 - If the HTTP version of the request is smaller than 1.1
 - If the request contains an Authorization header
 
-Caution!: Due to the current limitation of the filters, it is not recommended
-to use the cache with other filters. Using them can cause undefined behavior
-if they modify the response (compression for example).
+Caution!: For HAProxy version prior to 1.9, due to the limitation of the
+filters, it is not recommended to use the cache with other filters. Using them
+can cause undefined behavior if they modify the response (compression for
+example). For HAProxy 1.9 and greater, it is safe, for HTX proxies only (see
+"option http-use-htx" for details).
 
 10.2. Setup
 -----------
index fd8a00ad3d7b1bc00f9e26c149ba8dc9e11f45b0..407d150f9dcd9f9656df3ff3fd0df5867a79c40b 100644 (file)
@@ -45,6 +45,8 @@
 
 #define CACHE_FLT_F_IGNORE_CNT_ENC 0x00000001 /* Ignore 'Content-Encoding' header when response is cached
                                               * if compression is already started */
+#define CACHE_FLT_F_IMPLICIT_DECL  0x00000002 /* The cache filtre was implicitly declared (ie without
+                                              * the filter keyword) */
 
 const char *cache_store_flt_id = "cache store filter";
 
@@ -189,13 +191,20 @@ cache_store_check(struct proxy *px, struct flt_conf *fconf)
         * compressed in the cache. When the compression is after the cache, the
         * 'Content-encoding' header must be ignored because the response will
         * be stored uncompressed. The compression will be done on the cached
-        * response too. */
+        * response too. Also check if the cache filter must be explicitly
+        * declaired or not. */
        list_for_each_entry(f, &px->filter_configs, list) {
                if (f == fconf) {
                        ignore = 1;
                        continue;
                }
 
+               if ((f->id != fconf->id) && (cconf->flags & CACHE_FLT_F_IMPLICIT_DECL)) {
+                       ha_alert("config: %s '%s': require an explicit filter declaration "
+                                "to use the cache '%s'.\n", proxy_type_str(px), px->id, cache->id);
+                       return 1;
+               }
+
                if (f->id == http_comp_flt_id) {
                        if (!(px->options2 & PR_O2_USE_HTX)) {
                                ha_alert("config: %s '%s' : compression and cache filters cannot be "
@@ -1264,7 +1273,7 @@ static int parse_cache_rule(struct proxy *proxy, const char *name, struct act_ru
                memprintf(err, "out of memory\n");
                goto err;
        }
-       cconf->flags = 0;
+       cconf->flags = CACHE_FLT_F_IMPLICIT_DECL;
        cconf->c.name = strdup(name);
        if (!cconf->c.name) {
                memprintf(err, "out of memory\n");
@@ -1623,6 +1632,81 @@ struct flt_ops cache_ops = {
 
 };
 
+
+
+static int
+parse_cache_flt(char **args, int *cur_arg, struct proxy *px,
+               struct flt_conf *fconf, char **err, void *private)
+{
+       struct flt_conf *f, *back;
+       struct cache_flt_conf *cconf;
+       char *name = NULL;
+       int pos = *cur_arg;
+
+       /* Get the cache filter name*/
+       if (!strcmp(args[pos], "cache")) {
+               if (!*args[pos + 1]) {
+                       memprintf(err, "%s : expects an <id> argument", args[pos]);
+                       goto error;
+               }
+               name = strdup(args[pos + 1]);
+               if (!name) {
+                       memprintf(err, "%s '%s' : out of memory", args[pos], args[pos + 1]);
+                       goto error;
+               }
+               pos += 2;
+       }
+
+       /* Check if an implicit filter with the same name already exists. If so,
+        * we remove the implicit filter to use the explicit one. */
+       list_for_each_entry_safe(f, back, &px->filter_configs, list) {
+               if (f->id != cache_store_flt_id)
+                       continue;
+
+               cconf = f->conf;
+               if (strcmp(name, cconf->c.name)) {
+                       cconf = NULL;
+                       continue;
+               }
+
+               if (!(cconf->flags & CACHE_FLT_F_IMPLICIT_DECL)) {
+                       cconf = NULL;
+                       memprintf(err, "%s: multiple explicit declarations of the cache filter '%s'",
+                                 px->id, name);
+                       return -1;
+               }
+
+               /* Remove the implicit filter. <cconf> is kept for the explicit one */
+               LIST_DEL(&f->list);
+               free(f);
+               free(name);
+               break;
+       }
+
+       /* No implicit cache filter found, create configuration for the explicit one */
+       if (!cconf) {
+               cconf = calloc(1, sizeof(*cconf));
+               if (!cconf) {
+                       memprintf(err, "%s: out of memory", args[*cur_arg]);
+                       goto error;
+               }
+               cconf->c.name = name;
+       }
+
+       cconf->flags = 0;
+       fconf->id   = cache_store_flt_id;
+       fconf->conf = cconf;
+       fconf->ops  = &cache_ops;
+
+       *cur_arg = pos;
+       return 0;
+
+  error:
+       free(name);
+       free(cconf);
+       return -1;
+}
+
 static int cli_parse_show_cache(char **args, char *payload, struct appctx *appctx, void *private)
 {
        if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
@@ -1686,6 +1770,15 @@ static int cli_io_handler_show_cache(struct appctx *appctx)
 
 }
 
+/* Declare the filter parser for "cache" keyword */
+static struct flt_kw_list filter_kws = { "CACHE", { }, {
+               { "cache", parse_cache_flt, NULL },
+               { NULL, NULL, NULL },
+       }
+};
+
+INITCALL1(STG_REGISTER, flt_register_keywords, &filter_kws);
+
 static struct cli_kw_list cli_kws = {{},{
        { { "show", "cache", NULL }, "show cache     : show cache status", cli_parse_show_cache, cli_io_handler_show_cache, NULL, NULL },
        {{},}