From afd819c54a1151f748143ec9db6817d61072bdf5 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Tue, 11 Dec 2018 08:57:45 +0100 Subject: [PATCH] MEDIUM: cache/compression: Add a way to safely combined compression and cache This is only true for HTX proxies. On legacy HTTP proxy, if the compression and the cache are both enabled, an error during HAProxy startup is triggered. With the HTX, now you can use both in any order. If the compression is defined before the cache, then the responses will be stored compressed. If the compression is defined after the cache, then the responses will be stored uncompressed. So in the last case, when a response is served from the cache, it will compressed too like any response. --- src/cache.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/cache.c b/src/cache.c index 834efc184..fd8a00ad3 100644 --- a/src/cache.c +++ b/src/cache.c @@ -43,6 +43,9 @@ * messages (legacy implementation) */ #define CACHE_F_HTX 0x00000002 /* The cache is used to store HTX messages */ +#define CACHE_FLT_F_IGNORE_CNT_ENC 0x00000001 /* Ignore 'Content-Encoding' header when response is cached + * if compression is already started */ + const char *cache_store_flt_id = "cache store filter"; struct applet http_cache_applet; @@ -154,7 +157,9 @@ static int cache_store_check(struct proxy *px, struct flt_conf *fconf) { struct cache_flt_conf *cconf = fconf->conf; + struct flt_conf *f; struct cache *cache; + int ignore = 0; /* resolve the cache name to a ptr in the filter config */ list_for_each_entry(cache, &caches, list) { @@ -175,6 +180,34 @@ cache_store_check(struct proxy *px, struct flt_conf *fconf) return 1; found: + /* Here points on the cache the filter must use and + * points on the cache filter configuration. */ + + /* Check all filters for proxy to know if the compression is + * enabled and if it is before or after the cache. When the compression + * is before the cache, nothing special is done. The response is stored + * 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. */ + list_for_each_entry(f, &px->filter_configs, list) { + if (f == fconf) { + ignore = 1; + continue; + } + + 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 " + "both enabled on non HTX proxy.\n", + proxy_type_str(px), px->id); + return 1; + } + if (ignore) + cconf->flags |= CACHE_FLT_F_IGNORE_CNT_ENC; + break; + } + } return 0; } @@ -630,10 +663,26 @@ enum act_return http_action_store_cache(struct act_rule *rule, struct proxy *px, chunk_reset(&trash); for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { struct htx_blk *blk = htx_get_blk(htx, pos); + enum htx_blk_type type = htx_get_blk_type(blk); uint32_t sz = htx_get_blksz(blk); + /* Check if we need to skip 'Content-encoding' header or not */ + if ((msg->flags & HTTP_MSGF_COMPRESSING) && /* Compression in progress */ + (cconf->flags & CACHE_FLT_F_IGNORE_CNT_ENC) && /* Compression before the cache */ + (type == HTX_BLK_HDR)) { + struct ist n = htx_get_blk_name(htx, blk); + struct ist v = htx_get_blk_value(htx, blk); + + if (isteq(n, ist("content-encoding"))) + continue; + if (!(msg->flags & HTTP_MSGF_TE_CHNK) && + isteq(n, ist("transfer-encoding")) && + isteqi(v, ist("chunked"))) + continue; + } + chunk_memcat(&trash, (char *)&blk->info, sizeof(blk->info)); - if (htx_get_blk_type(blk) == HTX_BLK_EOH) + if (type == HTX_BLK_EOH) break; chunk_memcat(&trash, htx_get_blk_ptr(htx, blk), sz); } -- 2.47.3