MINOR: cache: Create res.cache_hit and res.cache_name sample fetches
Res.cache_hit sample fetch returns a boolean which is true when the HTTP
response was built out of a cache. The cache's name is returned by the
res.cache_name sample_fetch.
This resolves GitHub issue #900.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 3e16fac..c243369 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -18195,6 +18195,15 @@
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 0000000..1ba0690
--- /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 a98ca66..2c37ca1 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -28,6 +28,7 @@
#include <haproxy/htx.h>
#include <haproxy/net_helper.h>
#include <haproxy/proxy.h>
+#include <haproxy/sample.h>
#include <haproxy/shctx.h>
#include <haproxy/stream.h>
#include <haproxy/stream_interface.h>
@@ -1713,6 +1714,53 @@
}
+
+/*
+ * 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 @@
/* 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 <const> 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);