diff --git a/doc/configuration.txt b/doc/configuration.txt index 18fcf96040..400ec6402c 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -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 + + Arguments : + + 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 ----------- diff --git a/src/cache.c b/src/cache.c index fd8a00ad3d..407d150f9d 100644 --- a/src/cache.c +++ b/src/cache.c @@ -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 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. 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 }, {{},}