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.
This commit is contained in:
Remi Tricot-Le Breton 2020-10-27 11:55:57 +01:00 committed by William Lallemand
parent 53161d81b8
commit bf97121f1c
3 changed files with 198 additions and 0 deletions

View File

@ -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

130
reg-tests/cache/sample_fetches.vtc vendored Normal file
View File

@ -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

View File

@ -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 @@ 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 <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);