diff --git a/doc/configuration.txt b/doc/configuration.txt index 3e16fac505..c243369080 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -18195,6 +18195,15 @@ res.body_size : integer useful (and usable) in the health-check context. It may be used in tcp-check based expect rules. +res.cache_hit : boolean + Returns the boolean "true" value if the response has been built out of an + HTTP cache entry, otherwise returns boolean "false". + +res.cache_name : string + Returns a string containing the name of the HTTP cache that was used to + build the HTTP response if res.cache_hit is true, otherwise returns an + empty string. + res.comp : boolean Returns the boolean "true" value if the response has been compressed by HAProxy, otherwise returns boolean "false". This may be used to add diff --git a/reg-tests/cache/sample_fetches.vtc b/reg-tests/cache/sample_fetches.vtc new file mode 100644 index 0000000000..1ba0690221 --- /dev/null +++ b/reg-tests/cache/sample_fetches.vtc @@ -0,0 +1,130 @@ + +varnishtest "Basic cache test" + +#REQUIRE_VERSION=1.9 + +feature ignore_unknown_macro + +server s1 { + rxreq + txresp -nolen -hdr "Transfer-Encoding: chunked" + chunkedlen 15 + chunkedlen 15 + chunkedlen 15 + chunkedlen 0 +} -start + +server s2 { + rxreq + txresp -nolen -hdr "Transfer-Encoding: chunked" + chunkedlen 16 + chunkedlen 16 + chunkedlen 16 + chunkedlen 0 +} -start + +server s3 { + rxreq + txresp -nolen -hdr "Transfer-Encoding: chunked" + chunkedlen 17 + chunkedlen 17 + chunkedlen 17 + chunkedlen 0 + + rxreq + txresp -nolen -hdr "Transfer-Encoding: chunked" + chunkedlen 17 + chunkedlen 17 + chunkedlen 17 + chunkedlen 0 +} -start + +haproxy h1 -conf { + defaults + mode http + ${no-htx} option http-use-htx + timeout connect 1s + timeout client 1s + timeout server 1s + + frontend fe + bind "fd@${fe}" + use_backend first_be if { path_beg /first } + use_backend nocache_be if { path_beg /nocache } + default_backend second_be + + backend first_be + http-request cache-use first_cache + server www ${s1_addr}:${s1_port} + http-response cache-store first_cache + http-response set-header X-Cache-Hit %[res.cache_hit] + http-response set-header X-Cache-Name %[res.cache_name] + + backend second_be + http-request cache-use second_cache + server www ${s2_addr}:${s2_port} + http-response cache-store second_cache + http-response set-header X-Cache-Hit %[res.cache_hit] + http-response set-header X-Cache-Name %[res.cache_name] + + backend nocache_be + server www ${s3_addr}:${s3_port} + http-response set-header X-Cache-Hit %[res.cache_hit] + http-response set-header X-Cache-Name %[res.cache_name] + + cache first_cache + total-max-size 3 + max-age 40 + max-object-size 3000 + + cache second_cache + total-max-size 3 + max-age 20 + max-object-size 3072 +} -start + + +client c1 -connect ${h1_fe_sock} { + txreq -url "/first" + rxresp + expect resp.status == 200 + expect resp.bodylen == 45 + expect resp.http.X-Cache-Hit == 0 + expect resp.http.X-Cache-Name == "" + + txreq -url "/second" + rxresp + expect resp.status == 200 + expect resp.bodylen == 48 + expect resp.http.X-Cache-Hit == 0 + expect resp.http.X-Cache-Name == "" + + txreq -url "/nocache" + rxresp + expect resp.status == 200 + expect resp.bodylen == 51 + expect resp.http.X-Cache-Hit == 0 + expect resp.http.X-Cache-Name == "" + + # Response should come form the cache now + txreq -url "/nocache" + rxresp + expect resp.status == 200 + expect resp.bodylen == 51 + expect resp.http.X-Cache-Hit == 0 + expect resp.http.X-Cache-Name == "" + + txreq -url "/first" + rxresp + expect resp.status == 200 + expect resp.bodylen == 45 + expect resp.http.X-Cache-Hit == 1 + expect resp.http.X-Cache-Name == "first_cache" + + txreq -url "/second" + rxresp + expect resp.status == 200 + expect resp.bodylen == 48 + expect resp.http.X-Cache-Hit == 1 + expect resp.http.X-Cache-Name == "second_cache" +} -run diff --git a/src/cache.c b/src/cache.c index a98ca66e62..2c37ca13b5 100644 --- a/src/cache.c +++ b/src/cache.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1713,6 +1714,53 @@ static int cli_io_handler_show_cache(struct appctx *appctx) } + +/* + * boolean, returns true if response was built out of a cache entry. + */ +static int +smp_fetch_res_cache_hit(const struct arg *args, struct sample *smp, + const char *kw, void *private) +{ + smp->data.type = SMP_T_BOOL; + smp->data.u.sint = (smp->strm ? (smp->strm->target == &http_cache_applet.obj_type) : 0); + + return 1; +} + +/* + * string, returns cache name (if response came from a cache). + */ +static int +smp_fetch_res_cache_name(const struct arg *args, struct sample *smp, + const char *kw, void *private) +{ + struct appctx *appctx = NULL; + + struct cache_flt_conf *cconf = NULL; + struct cache *cache = NULL; + + if (!smp->strm || smp->strm->target != &http_cache_applet.obj_type) + return 0; + + /* Get appctx from the stream_interface. */ + appctx = si_appctx(&smp->strm->si[1]); + if (appctx && appctx->rule) { + cconf = appctx->rule->arg.act.p[0]; + if (cconf) { + cache = cconf->c.cache; + + smp->data.type = SMP_T_STR; + smp->flags = SMP_F_CONST; + smp->data.u.str.area = cache->id; + smp->data.u.str.data = strlen(cache->id); + return 1; + } + } + + return 0; +} + /* Declare the filter parser for "cache" keyword */ static struct flt_kw_list filter_kws = { "CACHE", { }, { { "cache", parse_cache_flt, NULL }, @@ -1757,3 +1805,14 @@ struct applet http_cache_applet = { /* config parsers for this section */ REGISTER_CONFIG_SECTION("cache", cfg_parse_cache, cfg_post_parse_section_cache); REGISTER_POST_CHECK(post_check_cache); + + +/* Note: must not be declared as its list will be overwritten */ +static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { + { "res.cache_hit", smp_fetch_res_cache_hit, 0, NULL, SMP_T_BOOL, SMP_USE_HRSHP, SMP_VAL_RESPONSE }, + { "res.cache_name", smp_fetch_res_cache_name, 0, NULL, SMP_T_STR, SMP_USE_HRSHP, SMP_VAL_RESPONSE }, + { /* END */ }, + } +}; + +INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);