MEDIUM: samples: use new flags to describe compatibility between fetches and their usages

Samples fetches were relying on two flags SMP_CAP_REQ/SMP_CAP_RES to describe
whether they were compatible with requests rules or with response rules. This
was never reliable because we need a finer granularity (eg: an HTTP request
method needs to parse an HTTP request, and is available past this point).

Some fetches are also dependant on the context (eg: "hdr" uses request or
response depending where it's involved, causing some abiguity).

In order to solve this, we need to precisely indicate in fetches what they
use, and their users will have to compare with what they have.

So now we have a bunch of bits indicating where the sample is fetched in the
processing chain, with a few variants indicating for some of them if it is
permanent or volatile (eg: an HTTP status is stored into the transaction so
it is permanent, despite being caught in the response contents).

The fetches also have a second mask indicating their validity domain. This one
is computed from a conversion table at registration time, so there is no need
for doing it by hand. This validity domain consists in a bitmask with one bit
set for each usage point in the processing chain. Some provisions were made
for upcoming controls such as connection-based TCP rules which apply on top of
the connection layer but before instantiating the session.

Then everywhere a fetch is used, the bit for the control point is checked in
the fetch's validity domain, and it becomes possible to finely ensure that a
fetch will work or not.

Note that we need these two separate bitfields because some fetches are usable
both in request and response (eg: "hdr", "payload"). So the keyword will have
a "use" field made of a combination of several SMP_USE_* values, which will be
converted into a wider list of SMP_VAL_* flags.

The knowledge of permanent vs dynamic information has disappeared for now, as
it was never used. Later we'll probably reintroduce it differently when
dealing with variables. Its only use at the moment could have been to avoid
caching a dynamic rate measurement, but nothing is cached as of now.
diff --git a/include/proto/sample.h b/include/proto/sample.h
index 77d6c18..3137948 100644
--- a/include/proto/sample.h
+++ b/include/proto/sample.h
@@ -34,5 +34,6 @@
                                    unsigned int opt, struct sample_expr *expr);
 void sample_register_fetches(struct sample_fetch_kw_list *psl);
 void sample_register_convs(struct sample_conv_kw_list *psl);
+const char *sample_src_names(unsigned int use);
 
 #endif /* _PROTO_SAMPLE_H */
diff --git a/include/types/sample.h b/include/types/sample.h
index 6c19ade..0e1cd78 100644
--- a/include/types/sample.h
+++ b/include/types/sample.h
@@ -3,7 +3,7 @@
  * Macros, variables and structures for sample management.
  *
  * Copyright (C) 2009-2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
- * Copyright (C) 2012 Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2012-2013 Willy Tarreau <w@1wt.eu>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -44,15 +44,112 @@
 	SMP_TYPES        /* number of types, must always be last */
 };
 
-/* Sample fetch capabilities are used to declare keywords. Right now only
- * the supportd fetch directions are specified.
+/* Sample sources are used to establish a relation between fetch keywords and
+ * the location where they're about to be used. They're reserved for internal
+ * use and are not meant to be known outside the sample management code.
  */
 enum {
-	SMP_CAP_REQ = 1 << 0, /* fetch supported on request */
-	SMP_CAP_RES = 1 << 1, /* fetch supported on response */
-	SMP_CAP_L7  = 1 << 2, /* fetch may require access to L7 */
+	SMP_SRC_INTRN,  /* internal context-less information */
+	SMP_SRC_LISTN,  /* listener which accepted the connection */
+	SMP_SRC_FTEND,  /* frontend which accepted the connection */
+	SMP_SRC_L4CLI,  /* L4 information about the client */
+	SMP_SRC_L5CLI,  /* fetch uses client information from embryonic session */
+	SMP_SRC_TRACK,  /* fetch involves track counters */
+	SMP_SRC_L6REQ,  /* fetch uses raw information from the request buffer */
+	SMP_SRC_HRQHV,  /* fetch uses volatile information about HTTP request headers (eg: value) */
+	SMP_SRC_HRQHP,  /* fetch uses persistent information about HTTP request headers (eg: meth) */
+	SMP_SRC_HRQBO,  /* fetch uses information about HTTP request body */
+	SMP_SRC_BKEND,  /* fetch uses information about the backend */
+	SMP_SRC_SERVR,  /* fetch uses information about the selected server */
+	SMP_SRC_L4SRV,  /* fetch uses information about the server L4 connection */
+	SMP_SRC_L5SRV,  /* fetch uses information about the server L5 connection */
+	SMP_SRC_L6RES,  /* fetch uses raw information from the response buffer */
+	SMP_SRC_HRSHV,  /* fetch uses volatile information about HTTP response headers (eg: value) */
+	SMP_SRC_HRSHP,  /* fetch uses persistent information about HTTP response headers (eg: status) */
+	SMP_SRC_HRSBO,  /* fetch uses information about HTTP response body */
+	SMP_SRC_RQFIN,  /* final information about request buffer (eg: tot bytes) */
+	SMP_SRC_RSFIN,  /* final information about response buffer (eg: tot bytes) */
+	SMP_SRC_TXFIN,  /* final information about the transaction (eg: #comp rate) */
+	SMP_SRC_SSFIN,  /* final information about the session (eg: #requests, final flags) */
+	SMP_SRC_ENTRIES /* nothing after this */
 };
 
+/* SMP_USE_* are flags used to declare fetch keywords. Fetch methods are
+ * associated with bitfields composed of these values, generally only one, to
+ * indicate where the contents may be sampled. Some fetches are ambiguous as
+ * they apply to either the request or the response depending on the context,
+ * so they will have 2 of these bits (eg: hdr(), payload(), ...). These are
+ * stored in smp->use.
+ */
+enum {
+	SMP_USE_INTRN = 1 << SMP_SRC_INTRN,  /* internal context-less information */
+	SMP_USE_LISTN = 1 << SMP_SRC_LISTN,  /* listener which accepted the connection */
+	SMP_USE_FTEND = 1 << SMP_SRC_FTEND,  /* frontend which accepted the connection */
+	SMP_USE_L4CLI = 1 << SMP_SRC_L4CLI,  /* L4 information about the client */
+	SMP_USE_L5CLI = 1 << SMP_SRC_L5CLI,  /* fetch uses client information from embryonic session */
+	SMP_USE_TRACK = 1 << SMP_SRC_TRACK,  /* fetch involves track counters */
+	SMP_USE_L6REQ = 1 << SMP_SRC_L6REQ,  /* fetch uses raw information from the request buffer */
+	SMP_USE_HRQHV = 1 << SMP_SRC_HRQHV,  /* fetch uses volatile information about HTTP request headers (eg: value) */
+	SMP_USE_HRQHP = 1 << SMP_SRC_HRQHP,  /* fetch uses persistent information about HTTP request headers (eg: meth) */
+	SMP_USE_HRQBO = 1 << SMP_SRC_HRQBO,  /* fetch uses information about HTTP request body */
+	SMP_USE_BKEND = 1 << SMP_SRC_BKEND,  /* fetch uses information about the backend */
+	SMP_USE_SERVR = 1 << SMP_SRC_SERVR,  /* fetch uses information about the selected server */
+	SMP_USE_L4SRV = 1 << SMP_SRC_L4SRV,  /* fetch uses information about the server L4 connection */
+	SMP_USE_L5SRV = 1 << SMP_SRC_L5SRV,  /* fetch uses information about the server L5 connection */
+	SMP_USE_L6RES = 1 << SMP_SRC_L6RES,  /* fetch uses raw information from the response buffer */
+	SMP_USE_HRSHV = 1 << SMP_SRC_HRSHV,  /* fetch uses volatile information about HTTP response headers (eg: value) */
+	SMP_USE_HRSHP = 1 << SMP_SRC_HRSHP,  /* fetch uses persistent information about HTTP response headers (eg: status) */
+	SMP_USE_HRSBO = 1 << SMP_SRC_HRSBO,  /* fetch uses information about HTTP response body */
+	SMP_USE_RQFIN = 1 << SMP_SRC_RQFIN,  /* final information about request buffer (eg: tot bytes) */
+	SMP_USE_RSFIN = 1 << SMP_SRC_RSFIN,  /* final information about response buffer (eg: tot bytes) */
+	SMP_USE_TXFIN = 1 << SMP_SRC_TXFIN,  /* final information about the transaction (eg: #comp rate) */
+	SMP_USE_SSFIN = 1 << SMP_SRC_SSFIN,  /* final information about the session (eg: #requests, final flags) */
+
+	/* This composite one is useful to detect if an hdr_idx needs to be allocated */
+	SMP_USE_HTTP_ANY = SMP_USE_HRQHV | SMP_USE_HRQHP | SMP_USE_HRQBO |
+	                   SMP_USE_HRSHV | SMP_USE_HRSHP | SMP_USE_HRSBO,
+};
+
+/* Sample validity is computed from the fetch sources above when keywords
+ * are registered. Each fetch method may be used at different locations. The
+ * configuration parser will check whether the fetches are compatible with the
+ * location where they're used. These are stored in smp->val.
+ */
+enum {
+	SMP_VAL___________ = 0,        /* Just used as a visual marker */
+	SMP_VAL_FE_CON_ACC = 1 <<  0,  /* FE connection accept rules ("tcp request connection") */
+	SMP_VAL_FE_SES_ACC = 1 <<  1,  /* FE session accept rules (to come soon) */
+	SMP_VAL_FE_REQ_CNT = 1 <<  2,  /* FE request content rules ("tcp request content") */
+	SMP_VAL_FE_HRQ_HDR = 1 <<  3,  /* FE HTTP request headers (rules, headers, monitor, stats, redirect) */
+	SMP_VAL_FE_HRQ_BDY = 1 <<  4,  /* FE HTTP request body */
+	SMP_VAL_FE_SET_BCK = 1 <<  5,  /* FE backend switching rules ("use_backend") */
+	SMP_VAL_BE_REQ_CNT = 1 <<  6,  /* BE request content rules ("tcp request content") */
+	SMP_VAL_BE_HRQ_HDR = 1 <<  7,  /* BE HTTP request headers (rules, headers, monitor, stats, redirect) */
+	SMP_VAL_BE_HRQ_BDY = 1 <<  8,  /* BE HTTP request body */
+	SMP_VAL_BE_SET_SRV = 1 <<  9,  /* BE server switching rules ("use_server", "balance", "force-persist", "stick", ...) */
+	SMP_VAL_BE_SRV_CON = 1 << 10,  /* BE server connect (eg: "source") */
+	SMP_VAL_BE_RES_CNT = 1 << 11,  /* BE response content rules ("tcp response content") */
+	SMP_VAL_BE_HRS_HDR = 1 << 12,  /* BE HTTP response headers (rules, headers) */
+	SMP_VAL_BE_HRS_BDY = 1 << 13,  /* BE HTTP response body (stick-store rules are there) */
+	SMP_VAL_BE_STO_RUL = 1 << 14,  /* BE stick-store rules */
+	SMP_VAL_FE_RES_CNT = 1 << 15,  /* FE response content rules ("tcp response content") */
+	SMP_VAL_FE_HRS_HDR = 1 << 16,  /* FE HTTP response headers (rules, headers) */
+	SMP_VAL_FE_HRS_BDY = 1 << 17,  /* FE HTTP response body */
+	SMP_VAL_FE_LOG_END = 1 << 18,  /* FE log at the end of the txn/session */
+
+	/* a few combinations to decide what direction to try to fetch (useful for logs) */
+	SMP_VAL_REQUEST    = SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
+	                     SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                     SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                     SMP_VAL_BE_SET_SRV,
+
+	SMP_VAL_RESPONSE   = SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT | SMP_VAL_BE_HRS_HDR |
+	                     SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL | SMP_VAL_FE_RES_CNT |
+	                     SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY | SMP_VAL_FE_LOG_END,
+};
+
+extern const unsigned int fetch_cap[SMP_SRC_ENTRIES];
+
 /* Sample fetch options are passed to sample fetch functions to add precision
  * about what is desired :
  *   - fetch direction (req/resp)
@@ -145,7 +242,8 @@
 	int (*val_args)(struct arg *arg_p,
 			char **err_msg);          /* argument validation function */
 	unsigned long out_type;                   /* output sample type */
-	unsigned int cap;                         /* fetch capabilities (SMP_CAP_*) */
+	unsigned int use;                         /* fetch source (SMP_USE_*) */
+	unsigned int val;                         /* fetch validity (SMP_VAL_*) */
 };
 
 /* sample expression */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 8c959de..a0178e6 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -3022,17 +3022,17 @@
 		}
 
 		if (flags & STK_ON_RSP) {
-			if (!(expr->fetch->cap & SMP_CAP_RES)) {
-				Alert("parsing [%s:%d] : '%s': fetch method '%s' can not be used on response.\n",
-				      file, linenum, args[0], expr->fetch->kw);
+			if (!(expr->fetch->val & SMP_VAL_BE_STO_RUL)) {
+				Alert("parsing [%s:%d] : '%s': fetch method '%s' extracts information from '%s', none of which is available for 'store-response'.\n",
+				      file, linenum, args[0], expr->fetch->kw, sample_src_names(expr->fetch->use));
 		                err_code |= ERR_ALERT | ERR_FATAL;
 				free(expr);
 			        goto out;
 			}
 		} else {
-			if (!(expr->fetch->cap & SMP_CAP_REQ)) {
-				Alert("parsing [%s:%d] : '%s': fetch method '%s' can not be used on request.\n",
-				      file, linenum, args[0], expr->fetch->kw);
+			if (!(expr->fetch->val & SMP_VAL_BE_SET_SRV)) {
+				Alert("parsing [%s:%d] : '%s': fetch method '%s' extracts information from '%s', none of which is available during request.\n",
+				      file, linenum, args[0], expr->fetch->kw, sample_src_names(expr->fetch->use));
 				err_code |= ERR_ALERT | ERR_FATAL;
 				free(expr);
 				goto out;
@@ -3040,7 +3040,7 @@
 		}
 
 		/* check if we need to allocate an hdr_idx struct for HTTP parsing */
-		if (expr->fetch->cap & SMP_CAP_L7)
+		if (expr->fetch->use & SMP_USE_HTTP_ANY)
 			curproxy->acl_requires |= ACL_USE_L7_ANY;
 
 		if (strcmp(args[myidx], "table") == 0) {
diff --git a/src/log.c b/src/log.c
index 338a986..45cc67e 100644
--- a/src/log.c
+++ b/src/log.c
@@ -335,15 +335,15 @@
 		node->arg = my_strndup(arg, arg_len);
 		parse_logformat_var_args(node->arg, node);
 	}
-	if (expr->fetch->cap & SMP_CAP_REQ)
+	if (expr->fetch->val & SMP_VAL_REQUEST)
 		node->options |= LOG_OPT_REQ_CAP; /* fetch method is request-compatible */
 
-	if (expr->fetch->cap & SMP_CAP_RES)
+	if (expr->fetch->val & SMP_VAL_RESPONSE)
 		node->options |= LOG_OPT_RES_CAP; /* fetch method is response-compatible */
 
 	/* check if we need to allocate an hdr_idx struct for HTTP parsing */
 	/* Note, we may also need to set curpx->to_log with certain fetches */
-	if (expr->fetch->cap & SMP_CAP_L7)
+	if (expr->fetch->use & SMP_USE_HTTP_ANY)
 		curpx->acl_requires |= ACL_USE_L7_ANY;
 
 	/* FIXME: temporary workaround for missing LW_XPRT flag needed with some
diff --git a/src/proto_http.c b/src/proto_http.c
index 0808e64..3696557 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -9598,17 +9598,17 @@
 /************************************************************************/
 /* Note: must not be declared <const> as its list will be overwritten */
 static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{
-	{ "hdr",        smp_fetch_hdr,            ARG2(1,STR,SINT), val_hdr, SMP_T_CSTR, SMP_CAP_L7|SMP_CAP_REQ },
-	{ "base",       smp_fetch_base,           0,                NULL,    SMP_T_CSTR, SMP_CAP_L7|SMP_CAP_REQ },
-	{ "base32",     smp_fetch_base32,         0,                NULL,    SMP_T_UINT, SMP_CAP_L7|SMP_CAP_REQ },
-	{ "base32+src", smp_fetch_base32_src,     0,                NULL,    SMP_T_BIN,  SMP_CAP_L7|SMP_CAP_REQ },
-	{ "path",       smp_fetch_path,           0,                NULL,    SMP_T_CSTR, SMP_CAP_L7|SMP_CAP_REQ },
-	{ "url",        smp_fetch_url,            0,                NULL,    SMP_T_CSTR, SMP_CAP_L7|SMP_CAP_REQ },
-	{ "url_ip",     smp_fetch_url_ip,         0,                NULL,    SMP_T_IPV4, SMP_CAP_L7|SMP_CAP_REQ },
-	{ "url_port",   smp_fetch_url_port,       0,                NULL,    SMP_T_UINT, SMP_CAP_L7|SMP_CAP_REQ },
-	{ "url_param",  smp_fetch_url_param,      ARG2(1,STR,STR),  NULL,    SMP_T_CSTR, SMP_CAP_L7|SMP_CAP_REQ },
-	{ "cookie",     smp_fetch_cookie,         ARG1(1,STR),      NULL,    SMP_T_CSTR, SMP_CAP_L7|SMP_CAP_REQ|SMP_CAP_RES },
-	{ "set-cookie", smp_fetch_cookie,         ARG1(1,STR),      NULL,    SMP_T_CSTR, SMP_CAP_L7|SMP_CAP_RES }, /* deprecated */
+	{ "hdr",        smp_fetch_hdr,            ARG2(1,STR,SINT), val_hdr, SMP_T_CSTR, SMP_USE_HRQHV|SMP_USE_HRSHV },
+	{ "base",       smp_fetch_base,           0,                NULL,    SMP_T_CSTR, SMP_USE_HRQHV },
+	{ "base32",     smp_fetch_base32,         0,                NULL,    SMP_T_UINT, SMP_USE_HRQHV },
+	{ "base32+src", smp_fetch_base32_src,     0,                NULL,    SMP_T_BIN,  SMP_USE_HRQHV },
+	{ "path",       smp_fetch_path,           0,                NULL,    SMP_T_CSTR, SMP_USE_HRQHV },
+	{ "url",        smp_fetch_url,            0,                NULL,    SMP_T_CSTR, SMP_USE_HRQHV },
+	{ "url_ip",     smp_fetch_url_ip,         0,                NULL,    SMP_T_IPV4, SMP_USE_HRQHV },
+	{ "url_port",   smp_fetch_url_port,       0,                NULL,    SMP_T_UINT, SMP_USE_HRQHV },
+	{ "url_param",  smp_fetch_url_param,      ARG2(1,STR,STR),  NULL,    SMP_T_CSTR, SMP_USE_HRQHV },
+	{ "cookie",     smp_fetch_cookie,         ARG1(1,STR),      NULL,    SMP_T_CSTR, SMP_USE_HRQHV|SMP_USE_HRSHV },
+	{ "set-cookie", smp_fetch_cookie,         ARG1(1,STR),      NULL,    SMP_T_CSTR, SMP_USE_HRSHV }, /* deprecated */
 	{ NULL, NULL, 0, 0, 0 },
 }};
 
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 1e1d076..7ad825e 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1043,7 +1043,8 @@
 /* Parse a tcp-response rule. Return a negative value in case of failure */
 static int tcp_parse_response_rule(char **args, int arg, int section_type,
 				  struct proxy *curpx, struct proxy *defpx,
-				  struct tcp_rule *rule, char **err)
+				  struct tcp_rule *rule, char **err,
+                                  unsigned int where)
 {
 	if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
 		memprintf(err, "%s %s is only allowed in 'backend' sections",
@@ -1087,8 +1088,9 @@
 
 /* Parse a tcp-request rule. Return a negative value in case of failure */
 static int tcp_parse_request_rule(char **args, int arg, int section_type,
-				  struct proxy *curpx, struct proxy *defpx,
-				  struct tcp_rule *rule, char **err)
+                                  struct proxy *curpx, struct proxy *defpx,
+                                  struct tcp_rule *rule, char **err,
+                                  unsigned int where)
 {
 	if (curpx == defpx) {
 		memprintf(err, "%s %s is not allowed in 'defaults' sections",
@@ -1118,16 +1120,16 @@
 			return -1;
 		}
 
-		if (!(expr->fetch->cap & SMP_CAP_REQ)) {
+		if (!(expr->fetch->val & where)) {
 			memprintf(err,
-			          "'%s %s %s' : fetch method '%s' cannot be used on request",
-			          args[0], args[1], args[kw], trash.str);
+			          "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
+			          args[0], args[1], args[kw], args[arg], sample_src_names(expr->fetch->use));
 			free(expr);
 			return -1;
 		}
 
 		/* check if we need to allocate an hdr_idx struct for HTTP parsing */
-		if (expr->fetch->cap & SMP_CAP_L7)
+		if (expr->fetch->use & SMP_USE_HTTP_ANY)
 			curpx->acl_requires |= ACL_USE_L7_ANY;
 
 		if (strcmp(args[arg], "table") == 0) {
@@ -1187,6 +1189,7 @@
 	int warn = 0;
 	int arg;
 	struct tcp_rule *rule;
+	unsigned int where;
 
 	if (!*args[1]) {
 		memprintf(err, "missing argument for '%s' in %s '%s'",
@@ -1222,10 +1225,17 @@
 	rule = calloc(1, sizeof(*rule));
 	LIST_INIT(&rule->list);
 	arg = 1;
+	where = 0;
 
 	if (strcmp(args[1], "content") == 0) {
 		arg++;
-		if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err) < 0)
+
+		if (curpx->cap & PR_CAP_FE)
+			where |= SMP_VAL_FE_RES_CNT;
+		if (curpx->cap & PR_CAP_BE)
+			where |= SMP_VAL_BE_RES_CNT;
+
+		if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where) < 0)
 			goto error;
 
 		if (rule->cond && (rule->cond->requires & ACL_USE_L6REQ_VOLATILE)) {
@@ -1269,6 +1279,7 @@
 	int warn = 0;
 	int arg;
 	struct tcp_rule *rule;
+	unsigned int where;
 
 	if (!*args[1]) {
 		if (curpx == defpx)
@@ -1307,10 +1318,17 @@
 	rule = calloc(1, sizeof(*rule));
 	LIST_INIT(&rule->list);
 	arg = 1;
+	where = 0;
 
 	if (strcmp(args[1], "content") == 0) {
 		arg++;
-		if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err) < 0)
+
+		if (curpx->cap & PR_CAP_FE)
+			where |= SMP_VAL_FE_REQ_CNT;
+		if (curpx->cap & PR_CAP_BE)
+			where |= SMP_VAL_BE_REQ_CNT;
+
+		if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where) < 0)
 			goto error;
 
 		if (rule->cond && (rule->cond->requires & ACL_USE_RTR_ANY)) {
@@ -1326,14 +1344,6 @@
 			warn++;
 		}
 
-		if ((rule->action == TCP_ACT_TRK_SC1 || rule->action == TCP_ACT_TRK_SC2) &&
-		    !(rule->act_prm.trk_ctr.expr->fetch->cap & SMP_CAP_REQ)) {
-			memprintf(err,
-			          "fetch '%s' cannot be used on requests and will be ignored in '%s %s'",
-			          rule->act_prm.trk_ctr.expr->fetch->kw, args[0], args[1]);
-			warn++;
-		}
-
 		LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
 	}
 	else if (strcmp(args[1], "connection") == 0) {
@@ -1345,7 +1355,9 @@
 			goto error;
 		}
 
-		if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err) < 0)
+		where |= SMP_VAL_FE_CON_ACC;
+
+		if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where) < 0)
 			goto error;
 
 		if (rule->cond && (rule->cond->requires & (ACL_USE_RTR_ANY|ACL_USE_L6_ANY|ACL_USE_L7_ANY))) {
@@ -1369,22 +1381,6 @@
 			warn++;
 		}
 
-		if ((rule->action == TCP_ACT_TRK_SC1 || rule->action == TCP_ACT_TRK_SC2) &&
-		    !(rule->act_prm.trk_ctr.expr->fetch->cap & SMP_CAP_REQ)) {
-			memprintf(err,
-			          "fetch '%s' cannot be used on requests and will be ignored in '%s %s'",
-			          rule->act_prm.trk_ctr.expr->fetch->kw, args[0], args[1]);
-			warn++;
-		}
-
-		if ((rule->action == TCP_ACT_TRK_SC1 || rule->action == TCP_ACT_TRK_SC2) &&
-		    (rule->act_prm.trk_ctr.expr->fetch->cap & SMP_CAP_L7)) {
-			memprintf(err,
-			          "fetch '%s' involves some layer7-only criteria which will be ignored in '%s %s'",
-			          rule->act_prm.trk_ctr.expr->fetch->kw, args[0], args[1]);
-			warn++;
-		}
-
 		LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
 	}
 	else {
@@ -1882,13 +1878,13 @@
  * instance v4/v6 must be declared v4.
  */
 static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{
-	{ "src",         smp_fetch_src,           0,                      NULL,           SMP_T_IPV4, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "dst",         smp_fetch_dst,           0,                      NULL,           SMP_T_IPV4, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "dst_port",    smp_fetch_dport,         0,                      NULL,           SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "payload",     smp_fetch_payload,       ARG2(2,UINT,UINT),      val_payload,    SMP_T_CBIN, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "payload_lv",  smp_fetch_payload_lv,    ARG3(2,UINT,UINT,SINT), val_payload_lv, SMP_T_CBIN, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "rdp_cookie",  smp_fetch_rdp_cookie,    ARG1(1,STR),            NULL,           SMP_T_CSTR, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "src_port",    smp_fetch_sport,         0,                      NULL,           SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
+	{ "dst",         smp_fetch_dst,           0,                      NULL,           SMP_T_IPV4, SMP_USE_L4CLI },
+	{ "dst_port",    smp_fetch_dport,         0,                      NULL,           SMP_T_UINT, SMP_USE_L4CLI },
+	{ "payload",     smp_fetch_payload,       ARG2(2,UINT,UINT),      val_payload,    SMP_T_CBIN, SMP_USE_L6REQ|SMP_USE_L6RES },
+	{ "payload_lv",  smp_fetch_payload_lv,    ARG3(2,UINT,UINT,SINT), val_payload_lv, SMP_T_CBIN, SMP_USE_L6REQ|SMP_USE_L6RES },
+	{ "rdp_cookie",  smp_fetch_rdp_cookie,    ARG1(1,STR),            NULL,           SMP_T_CSTR, SMP_USE_L6REQ },
+	{ "src",         smp_fetch_src,           0,                      NULL,           SMP_T_IPV4, SMP_USE_L4CLI },
+	{ "src_port",    smp_fetch_sport,         0,                      NULL,           SMP_T_UINT, SMP_USE_L4CLI },
 	{ NULL, NULL, 0, 0, 0 },
 }};
 
diff --git a/src/sample.c b/src/sample.c
index b3898e0..c35e833 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -36,13 +36,249 @@
 	.list = LIST_HEAD_INIT(sample_convs.list)
 };
 
+const unsigned int fetch_cap[SMP_SRC_ENTRIES] = {
+	[SMP_SRC_INTRN] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
+	                   SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_LISTN] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
+	                   SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_FTEND] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
+	                   SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_L4CLI] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
+	                   SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_L5CLI] = (SMP_VAL___________ | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
+	                   SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_TRACK] = (SMP_VAL_FE_CON_ACC | SMP_VAL_FE_SES_ACC | SMP_VAL_FE_REQ_CNT |
+	                   SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_L6REQ] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT |
+	                   SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________),
+
+	[SMP_SRC_HRQHV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT |
+	                   SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________),
+
+	[SMP_SRC_HRQHP] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_FE_REQ_CNT |
+	                   SMP_VAL_FE_HRQ_HDR | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_HRQBO] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL_FE_HRQ_BDY | SMP_VAL_FE_SET_BCK |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________),
+
+	[SMP_SRC_BKEND] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL_BE_REQ_CNT | SMP_VAL_BE_HRQ_HDR | SMP_VAL_BE_HRQ_BDY |
+	                   SMP_VAL_BE_SET_SRV | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_SERVR] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL_BE_SRV_CON | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_L4SRV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_L5SRV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_L6RES] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL___________),
+
+	[SMP_SRC_HRSHV] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL___________),
+
+	[SMP_SRC_HRSHP] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL_BE_RES_CNT |
+	                   SMP_VAL_BE_HRS_HDR | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_HRSBO] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL_BE_HRS_BDY | SMP_VAL_BE_STO_RUL |
+	                   SMP_VAL_FE_RES_CNT | SMP_VAL_FE_HRS_HDR | SMP_VAL_FE_HRS_BDY |
+	                   SMP_VAL___________),
+
+	[SMP_SRC_RQFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_RSFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_TXFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL_FE_LOG_END),
+
+	[SMP_SRC_SSFIN] = (SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL___________ | SMP_VAL___________ | SMP_VAL___________ |
+	                   SMP_VAL_FE_LOG_END),
+};
+
+static const char *fetch_src_names[SMP_SRC_ENTRIES] = {
+	[SMP_SRC_INTRN] = "internal state",
+	[SMP_SRC_LISTN] = "listener",
+	[SMP_SRC_FTEND] = "frontend",
+	[SMP_SRC_L4CLI] = "client address",
+	[SMP_SRC_L5CLI] = "client-side connection",
+	[SMP_SRC_TRACK] = "track counters",
+	[SMP_SRC_L6REQ] = "request buffer",
+	[SMP_SRC_HRQHV] = "HTTP request headers",
+	[SMP_SRC_HRQHP] = "HTTP request",
+	[SMP_SRC_HRQBO] = "HTTP request body",
+	[SMP_SRC_BKEND] = "backend",
+	[SMP_SRC_SERVR] = "server",
+	[SMP_SRC_L4SRV] = "server address",
+	[SMP_SRC_L5SRV] = "server-side connection",
+	[SMP_SRC_L6RES] = "response buffer",
+	[SMP_SRC_HRSHV] = "HTTP response headers",
+	[SMP_SRC_HRSHP] = "HTTP response",
+	[SMP_SRC_HRSBO] = "HTTP response body",
+	[SMP_SRC_RQFIN] = "request buffer statistics",
+	[SMP_SRC_RSFIN] = "response buffer statistics",
+	[SMP_SRC_TXFIN] = "transaction statistics",
+	[SMP_SRC_SSFIN] = "session statistics",
+};
+
+/* fill the trash with a comma-delimited list of source names for the <use> bit
+ * field which must be composed of a non-null set of SMP_USE_* flags. The return
+ * value is the pointer to the string in the trash buffer.
+ */
+const char *sample_src_names(unsigned int use)
+{
+	int bit;
+
+	trash.len = 0;
+	trash.str[0] = '\0';
+	for (bit = 0; bit < SMP_SRC_ENTRIES; bit++) {
+		if (!(use & ~((1 << bit) - 1)))
+			break; /* no more bits */
+
+		if (!(use & (1 << bit)))
+			continue; /* bit not set */
+
+		trash.len += snprintf(trash.str + trash.len, trash.size - trash.len, "%s%s",
+				      (use & ((1 << bit) - 1)) ? "," : "",
+		                      fetch_src_names[bit]);
+	}
+	return trash.str;
+}
+
 /*
- * Registers the sample fetch keyword list <kwl> as a list of valid keywords for next
- * parsing sessions.
+ * Registers the sample fetch keyword list <kwl> as a list of valid keywords
+ * for next parsing sessions. The fetch keywords capabilities are also computed
+ * from their ->use field.
  */
-void sample_register_fetches(struct sample_fetch_kw_list *pfkl)
+void sample_register_fetches(struct sample_fetch_kw_list *kwl)
 {
-	LIST_ADDQ(&sample_fetches.list, &pfkl->list);
+	struct sample_fetch *sf;
+	int bit;
+
+	for (sf = kwl->kw; sf->kw != NULL; sf++) {
+		for (bit = 0; bit < SMP_SRC_ENTRIES; bit++)
+			if (sf->use & (1 << bit))
+				sf->val |= fetch_cap[bit];
+	}
+	LIST_ADDQ(&sample_fetches.list, &kwl->list);
 }
 
 /*
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index b7ca2b4..590c353 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -2927,39 +2927,39 @@
  * Please take care of keeping this list alphabetically sorted.
  */
 static struct sample_fetch_kw_list sample_fetch_keywords = {{ },{
-	{ "ssl_c_ca_err",           smp_fetch_ssl_c_ca_err,       0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_ca_err_depth",     smp_fetch_ssl_c_ca_err_depth, 0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_err",              smp_fetch_ssl_c_err,          0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_i_dn",             smp_fetch_ssl_c_i_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_key_alg",          smp_fetch_ssl_c_key_alg,      0,    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_notafter",         smp_fetch_ssl_c_notafter,     0,    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_notbefore",        smp_fetch_ssl_c_notbefore,    0,    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_sig_alg",          smp_fetch_ssl_c_sig_alg,      0,    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_s_dn",             smp_fetch_ssl_c_s_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_serial",           smp_fetch_ssl_c_serial,       0,    NULL,    SMP_T_BIN,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_used",             smp_fetch_ssl_c_used,         0,    NULL,    SMP_T_BOOL, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_verify",           smp_fetch_ssl_c_verify,       0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_c_version",          smp_fetch_ssl_c_version,      0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_f_i_dn",             smp_fetch_ssl_f_i_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_f_key_alg",          smp_fetch_ssl_f_key_alg,      0,    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_f_notafter",         smp_fetch_ssl_f_notafter,     0,    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_f_notbefore",        smp_fetch_ssl_f_notbefore,    0,    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_f_sig_alg",          smp_fetch_ssl_f_sig_alg,      0,    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_f_s_dn",             smp_fetch_ssl_f_s_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_f_serial",           smp_fetch_ssl_f_serial,       0,    NULL,    SMP_T_BIN, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_f_version",          smp_fetch_ssl_f_version,      0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_fc",                 smp_fetch_ssl_fc,             0,    NULL,    SMP_T_BOOL, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_fc_alg_keysize",     smp_fetch_ssl_fc_alg_keysize, 0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_fc_cipher",          smp_fetch_ssl_fc_cipher,      0,    NULL,    SMP_T_CSTR, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_fc_has_crt",         smp_fetch_ssl_fc_has_crt,     0,    NULL,    SMP_T_BOOL, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_fc_has_sni",         smp_fetch_ssl_fc_has_sni,     0,    NULL,    SMP_T_BOOL, SMP_CAP_REQ|SMP_CAP_RES },
+	{ "ssl_c_ca_err",           smp_fetch_ssl_c_ca_err,       0,                   NULL,    SMP_T_UINT, SMP_USE_L5CLI },
+	{ "ssl_c_ca_err_depth",     smp_fetch_ssl_c_ca_err_depth, 0,                   NULL,    SMP_T_UINT, SMP_USE_L5CLI },
+	{ "ssl_c_err",              smp_fetch_ssl_c_err,          0,                   NULL,    SMP_T_UINT, SMP_USE_L5CLI },
+	{ "ssl_c_i_dn",             smp_fetch_ssl_c_i_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_c_key_alg",          smp_fetch_ssl_c_key_alg,      0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_c_notafter",         smp_fetch_ssl_c_notafter,     0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_c_notbefore",        smp_fetch_ssl_c_notbefore,    0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_c_sig_alg",          smp_fetch_ssl_c_sig_alg,      0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_c_s_dn",             smp_fetch_ssl_c_s_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_c_serial",           smp_fetch_ssl_c_serial,       0,                   NULL,    SMP_T_BIN,  SMP_USE_L5CLI },
+	{ "ssl_c_used",             smp_fetch_ssl_c_used,         0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
+	{ "ssl_c_verify",           smp_fetch_ssl_c_verify,       0,                   NULL,    SMP_T_UINT, SMP_USE_L5CLI },
+	{ "ssl_c_version",          smp_fetch_ssl_c_version,      0,                   NULL,    SMP_T_UINT, SMP_USE_L5CLI },
+	{ "ssl_f_i_dn",             smp_fetch_ssl_f_i_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_f_key_alg",          smp_fetch_ssl_f_key_alg,      0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_f_notafter",         smp_fetch_ssl_f_notafter,     0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_f_notbefore",        smp_fetch_ssl_f_notbefore,    0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_f_sig_alg",          smp_fetch_ssl_f_sig_alg,      0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_f_s_dn",             smp_fetch_ssl_f_s_dn,         ARG2(0,STR,SINT),    NULL,    SMP_T_STR,  SMP_USE_L5CLI },
+	{ "ssl_f_serial",           smp_fetch_ssl_f_serial,       0,                   NULL,    SMP_T_BIN,  SMP_USE_L5CLI },
+	{ "ssl_f_version",          smp_fetch_ssl_f_version,      0,                   NULL,    SMP_T_UINT, SMP_USE_L5CLI },
+	{ "ssl_fc",                 smp_fetch_ssl_fc,             0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
+	{ "ssl_fc_alg_keysize",     smp_fetch_ssl_fc_alg_keysize, 0,                   NULL,    SMP_T_UINT, SMP_USE_L5CLI },
+	{ "ssl_fc_cipher",          smp_fetch_ssl_fc_cipher,      0,                   NULL,    SMP_T_CSTR, SMP_USE_L5CLI },
+	{ "ssl_fc_has_crt",         smp_fetch_ssl_fc_has_crt,     0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
+	{ "ssl_fc_has_sni",         smp_fetch_ssl_fc_has_sni,     0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
 #ifdef OPENSSL_NPN_NEGOTIATED
-	{ "ssl_fc_npn",             smp_fetch_ssl_fc_npn,         0,    NULL,    SMP_T_CSTR, SMP_CAP_REQ|SMP_CAP_RES },
+	{ "ssl_fc_npn",             smp_fetch_ssl_fc_npn,         0,                   NULL,    SMP_T_CSTR, SMP_USE_L5CLI },
 #endif
-	{ "ssl_fc_protocol",        smp_fetch_ssl_fc_protocol,    0,    NULL,    SMP_T_CSTR, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_fc_use_keysize",     smp_fetch_ssl_fc_use_keysize, 0,    NULL,    SMP_T_UINT, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_fc_session_id",      smp_fetch_ssl_fc_session_id,  0,    NULL,    SMP_T_CBIN, SMP_CAP_REQ|SMP_CAP_RES },
-	{ "ssl_fc_sni",             smp_fetch_ssl_fc_sni,         0,    NULL,    SMP_T_CSTR, SMP_CAP_REQ|SMP_CAP_RES },
+	{ "ssl_fc_protocol",        smp_fetch_ssl_fc_protocol,    0,                   NULL,    SMP_T_CSTR, SMP_USE_L5CLI },
+	{ "ssl_fc_use_keysize",     smp_fetch_ssl_fc_use_keysize, 0,                   NULL,    SMP_T_UINT, SMP_USE_L5CLI },
+	{ "ssl_fc_session_id",      smp_fetch_ssl_fc_session_id,  0,                   NULL,    SMP_T_CBIN, SMP_USE_L5CLI },
+	{ "ssl_fc_sni",             smp_fetch_ssl_fc_sni,         0,                   NULL,    SMP_T_CSTR, SMP_USE_L5CLI },
 	{ NULL, NULL, 0, 0, 0 },
 }};