BUG/MAJOR: http_fetch: Get the channel depending on the keyword used

All HTTP samples are buggy because the channel tested in the prefetch functions
(HTX and legacy HTTP) is chosen depending on the sample direction and not the
keyword really used. It means the request channel is used if the sample is
called during the request analysis and the response channel is used if it is
called during the response analysis, regardless the sample really called. For
instance, if you use the sample "req.ver" in an http-response rule, the response
channel will be prefeched because it is called during the response analysis,
while the request channel should have been used instead. So some assumptions on
the validity of the sample may be made on the wrong channel. It is the first
bug.

Then the same error is done in some samples themselves. So fetches are performed
on the wrong channel. For instance, the header extraction (req.fhdr, res.fhdr,
req.hdr, res.hdr...). If the sample "req.hdr" is used in an http-response rule,
then the matching is done on the response headers and not the request ones. It
is the second bug.

Finally, the last one but not the least, in some samples, the right channel is
used. But because the prefetch was done on the wrong one, this channel may be in
a undefined state. For instance, using the sample "req.ver" in an http-response
rule leads to a matching on a posibility released buffer.

To fix all these bugs, the right channel is now chosen in sample fetches, before
the prefetch. If the same function is used to fetch requests and responses
elements, then the keyword is used to choose the right one. This channel is then
used by the functions smp_prefetch_htx() and smp_prefetch_http(). Of course, it
is also used by the samples themselves to extract information.

This patch must be backported to all supported versions. For version 1.8 and
priors, it must be totally refactored. First because there is no HTX into these
versions. Then the buffers API has changed in HAProxy 1.9. The files
http_fetch.{ch} doesn't exist on old versions.
diff --git a/include/proto/http_fetch.h b/include/proto/http_fetch.h
index 6f6cc23..e7623b4 100644
--- a/include/proto/http_fetch.h
+++ b/include/proto/http_fetch.h
@@ -30,17 +30,17 @@
 /* Note: these functions *do* modify the sample. Even in case of success, at
  * least the type and uint value are modified.
  */
-#define CHECK_HTTP_MESSAGE_FIRST() \
-	do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 1); if (r <= 0) return r; } while (0)
+#define CHECK_HTTP_MESSAGE_FIRST(chn) \
+	do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, (chn), smp, 1); if (r <= 0) return r; } while (0)
 
-#define CHECK_HTTP_MESSAGE_FIRST_PERM() \
-	do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 0); if (r <= 0) return r; } while (0)
+#define CHECK_HTTP_MESSAGE_FIRST_PERM(chn) \
+	do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, (chn), smp, 0); if (r <= 0) return r; } while (0)
 
 int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt,
-                  const struct arg *args, struct sample *smp, int req_vol);
+                  struct channel *chn, struct sample *smp, int req_vol);
 
 struct htx;
-struct htx *smp_prefetch_htx(struct sample *smp, const struct arg *args);
+struct htx *smp_prefetch_htx(struct sample *smp, struct channel *chn);
 
 int val_hdr(struct arg *arg, char **err_msg);
 
diff --git a/src/http_fetch.c b/src/http_fetch.c
index d720b37..599773a 100644
--- a/src/http_fetch.c
+++ b/src/http_fetch.c
@@ -47,6 +47,8 @@
 static THREAD_LOCAL struct hdr_ctx static_hdr_ctx;
 static THREAD_LOCAL struct http_hdr_ctx static_http_hdr_ctx;
 
+#define SMP_REQ_CHN(smp) (smp->strm ? &smp->strm->req : NULL)
+#define SMP_RES_CHN(smp) (smp->strm ? &smp->strm->res : NULL)
 
 /*
  * Returns the data from Authorization header. Function may be called more
@@ -167,19 +169,19 @@
  *     we'll never have any HTTP message there ;
  *   The HTX message if ready
  */
-struct htx *smp_prefetch_htx(struct sample *smp, const struct arg *args)
+struct htx *smp_prefetch_htx(struct sample *smp, struct channel *chn)
 {
 	struct stream *s = smp->strm;
-	unsigned int opt = smp->opt;
 	struct http_txn *txn = NULL;
 	struct htx *htx = NULL;
+	struct http_msg *msg;
 	struct htx_sl *sl;
 
 	/* Note: it is possible that <s> is NULL when called before stream
 	 * initialization (eg: tcp-request connection), so this function is the
 	 * one responsible for guarding against this case for all HTTP users.
 	 */
-	if (!s)
+	if (!s || !chn)
 		return NULL;
 
 	if (!s->txn) {
@@ -188,109 +190,101 @@
 		http_init_txn(s);
 		txn = s->txn;
 	}
+	txn = s->txn;
+	msg = (!(chn->flags & CF_ISRESP) ? &txn->req : &txn->rsp);
+	smp->data.type = SMP_T_BOOL;
 
 	if (IS_HTX_STRM(s)) {
-		if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
-			htx = htxbuf(&s->req.buf);
-			if (htx_is_empty(htx) || htx_get_tail_type(htx) < HTX_BLK_EOH) {
-				/* Parsing is done by the mux, just wait */
-				smp->flags |= SMP_F_MAY_CHANGE;
-				return NULL;
-			}
+		htx = htxbuf(&chn->buf);
 
-			/* OK we just got a valid HTTP request. We have some
-			 * minor preparation to perform so that further checks
-			 * can rely on HTTP tests.
-			 */
-			if (txn) {
-				sl = http_find_stline(htx);
-				txn->meth = sl->info.req.meth;
-				if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
-					s->flags |= SF_REDIRECTABLE;
-			}
+		if (msg->msg_state == HTTP_MSG_ERROR || (htx->flags & HTX_FL_PARSING_ERROR))
+			return NULL;
 
-			/* otherwise everything's ready for the request */
-		}
-		else {
-			htx = htxbuf(&s->res.buf);
+		if (msg->msg_state < HTTP_MSG_BODY) {
+			/* Analyse not yet started */
 			if (htx_is_empty(htx) || htx_get_tail_type(htx) < HTX_BLK_EOH) {
 				/* Parsing is done by the mux, just wait */
 				smp->flags |= SMP_F_MAY_CHANGE;
 				return NULL;
 			}
 		}
+		sl = http_find_stline(htx);
+		if (!sl) {
+			/* The start-line was already forwarded, it is too late to fetch anything */
+			return NULL;
+		}
 	}
 	else { /* RAW mode */
-		if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
-			struct buffer *buf;
-			struct h1m h1m;
-			struct http_hdr hdrs[MAX_HTTP_HDR];
-			union h1_sl h1sl;
-			unsigned int flags = HTX_FL_NONE;
-			int ret;
+		struct buffer *buf;
+		struct h1m h1m;
+		struct http_hdr hdrs[MAX_HTTP_HDR];
+		union h1_sl h1sl;
+		unsigned int flags = HTX_FL_NONE;
+		int ret;
 
-			buf = &s->req.buf;
-			if (b_head(buf) + b_data(buf) > b_wrap(buf))
-				b_slow_realign(buf, trash.area, 0);
+		/* no HTTP fetch on the response in TCP mode */
+		if (chn->flags & CF_ISRESP)
+			return NULL;
 
-			h1m_init_req(&h1m);
-			ret = h1_headers_to_hdr_list(b_head(buf), b_stop(buf),
-						     hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
-			if (ret <= 0) {
-				/* Invalid or too big*/
-				if (ret < 0 || channel_full(&s->req, global.tune.maxrewrite))
-					return NULL;
+		/* Now we are working on the request only */
+		buf = &chn->buf;
+		if (b_head(buf) + b_data(buf) > b_wrap(buf))
+			b_slow_realign(buf, trash.area, 0);
 
-				/* wait for a full request */
-				smp->flags |= SMP_F_MAY_CHANGE;
+		h1m_init_req(&h1m);
+		ret = h1_headers_to_hdr_list(b_head(buf), b_stop(buf),
+					     hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
+		if (ret <= 0) {
+			/* Invalid or too big*/
+			if (ret < 0 || channel_full(&s->req, global.tune.maxrewrite))
 				return NULL;
-			}
-
-			/* OK we just got a valid HTTP request. We have to
-			 * convert it into an HTX message.
-			 */
-			if (unlikely(h1sl.rq.v.len == 0)) {
-				/* try to convert HTTP/0.9 requests to HTTP/1.0 */
-				if (h1sl.rq.meth != HTTP_METH_GET || !h1sl.rq.u.len)
-					return NULL;
-				h1sl.rq.v = ist("HTTP/1.0");
-			}
-			else if ((h1sl.rq.v.len == 8) &&
-				 ((*(h1sl.rq.v.ptr + 5) > '1') ||
-				  ((*(h1sl.rq.v.ptr + 5) == '1') && (*(h1sl.rq.v.ptr + 7) >= '1'))))
-				h1m.flags |= H1_MF_VER_11;
-
 
-			/* Set HTX start-line flags */
-			if (h1m.flags & H1_MF_VER_11)
-				flags |= HTX_SL_F_VER_11;
-			if (h1m.flags & H1_MF_XFER_ENC)
-				flags |= HTX_SL_F_XFER_ENC;
-			if (h1m.flags & H1_MF_XFER_LEN) {
-				flags |= HTX_SL_F_XFER_LEN;
-				if (h1m.flags & H1_MF_CHNK)
-					flags |= HTX_SL_F_CHNK;
-				else if (h1m.flags & H1_MF_CLEN)
-					flags |= HTX_SL_F_CLEN;
-			}
+			/* wait for a full request */
+			smp->flags |= SMP_F_MAY_CHANGE;
+			return NULL;
+		}
 
-			htx = htx_from_buf(get_trash_chunk());
-			sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, h1sl.rq.m, h1sl.rq.u, h1sl.rq.v);
-			if (!sl || !htx_add_all_headers(htx, hdrs))
+		/* OK we just got a valid HTTP mesage. We have to convert it
+		 * into an HTX message.
+		 */
+		if (unlikely(h1sl.rq.v.len == 0)) {
+			/* try to convert HTTP/0.9 requests to HTTP/1.0 */
+			if (h1sl.rq.meth != HTTP_METH_GET || !h1sl.rq.u.len)
 				return NULL;
-			sl->info.req.meth = h1sl.rq.meth;
-
-			if (txn) {
-				txn->meth = h1sl.rq.meth;
-				if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
-					s->flags |= SF_REDIRECTABLE;
-			}
-			/* Ok, now everything's ready for the request */
+			h1sl.rq.v = ist("HTTP/1.0");
 		}
-		else {
-			/* Impossible, no HTTP fetch on tcp-response */
+
+		/* Set HTX start-line flags */
+		if (h1m.flags & H1_MF_VER_11)
+			flags |= HTX_SL_F_VER_11;
+		if (h1m.flags & H1_MF_XFER_ENC)
+			flags |= HTX_SL_F_XFER_ENC;
+		flags |= HTX_SL_F_XFER_LEN;
+		if (h1m.flags & H1_MF_CHNK)
+			flags |= HTX_SL_F_CHNK;
+		else if (h1m.flags & H1_MF_CLEN)
+			flags |= HTX_SL_F_CLEN;
+
+		htx = htx_from_buf(get_trash_chunk());
+		sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, h1sl.rq.m, h1sl.rq.u, h1sl.rq.v);
+		if (!sl || !htx_add_all_headers(htx, hdrs))
 			return NULL;
+	}
+
+	/* OK we just got a valid HTTP message. If not already done by
+	 * HTTP analyzers, we have some minor preparation to perform so
+	 * that further checks can rely on HTTP tests.
+	 */
+	if (sl && msg->msg_state < HTTP_MSG_BODY) {
+		if (!(chn->flags & CF_ISRESP)) {
+			txn->meth = sl->info.req.meth;
+			if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
+				s->flags |= SF_REDIRECTABLE;
 		}
+		else
+			txn->status = sl->info.res.status;
+		if (sl->flags & HTX_SL_F_VER_11)
+			msg->flags |= HTTP_MSGF_VER_11;
 	}
 
 	/* everything's OK */
@@ -312,7 +306,7 @@
  *   1 if an HTTP message is ready
  */
 int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt,
-                      const struct arg *args, struct sample *smp, int req_vol)
+                      struct channel *chn, struct sample *smp, int req_vol)
 {
 	struct http_txn *txn;
 	struct http_msg *msg;
@@ -321,7 +315,7 @@
 	 * initialization (eg: tcp-request connection), so this function is the
 	 * one responsible for guarding against this case for all HTTP users.
 	 */
-	if (!s)
+	if (!s || !chn)
 		return 0;
 
 	if (!s->txn) {
@@ -330,78 +324,77 @@
 		http_init_txn(s);
 	}
 	txn = s->txn;
-	msg = &txn->req;
-
-	/* Check for a dependency on a request */
 	smp->data.type = SMP_T_BOOL;
 
-	if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
-		/* If the buffer does not leave enough free space at the end,
-		 * we must first realign it.
-		 */
-		if (ci_head(&s->req) > b_orig(&s->req.buf) &&
-		    ci_head(&s->req) + ci_data(&s->req) > b_wrap(&s->req.buf) - global.tune.maxrewrite)
-			channel_slow_realign(&s->req, trash.area);
-
-		if (unlikely(txn->req.msg_state < HTTP_MSG_BODY)) {
-			if (msg->msg_state == HTTP_MSG_ERROR)
-				return 0;
+	if (chn->flags & CF_ISRESP) {
+		/* Check for a dependency on a response */
+		if (txn->rsp.msg_state < HTTP_MSG_BODY) {
+			smp->flags |= SMP_F_MAY_CHANGE;
+			return 0;
+		}
+		goto end;
+	}
 
-			/* Try to decode HTTP request */
-			if (likely(msg->next < ci_data(&s->req)))
-				http_msg_analyzer(msg, &txn->hdr_idx);
+	/* Check for a dependency on a request */
+	msg = &txn->req;
 
-			/* Still no valid request ? */
-			if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
-				if ((msg->msg_state == HTTP_MSG_ERROR) ||
-				    channel_full(&s->req, global.tune.maxrewrite)) {
-					return 0;
-				}
-				/* wait for final state */
-				smp->flags |= SMP_F_MAY_CHANGE;
-				return 0;
-			}
+	if (req_vol && (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) {
+		return 0;  /* data might have moved and indexes changed */
+	}
 
-			/* OK we just got a valid HTTP request. We have some minor
-			 * preparation to perform so that further checks can rely
-			 * on HTTP tests.
-			 */
+	/* If the buffer does not leave enough free space at the end, we must
+	 * first realign it.
+	 */
+	if (ci_head(chn) > b_orig(&chn->buf) &&
+	    ci_head(chn) + ci_data(chn) > b_wrap(&chn->buf) - global.tune.maxrewrite)
+		channel_slow_realign(chn, trash.area);
 
-			/* If the request was parsed but was too large, we must absolutely
-			 * return an error so that it is not processed. At the moment this
-			 * cannot happen, but if the parsers are to change in the future,
-			 * we want this check to be maintained.
-			 */
-			if (unlikely(ci_head(&s->req) + ci_data(&s->req) >
-				     b_wrap(&s->req.buf) - global.tune.maxrewrite)) {
-				msg->err_state = msg->msg_state;
-				msg->msg_state = HTTP_MSG_ERROR;
-				smp->data.u.sint = 1;
-				return 1;
-			}
+	if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
+		if (msg->msg_state == HTTP_MSG_ERROR)
+			return 0;
 
-			txn->meth = find_http_meth(ci_head(msg->chn), msg->sl.rq.m_l);
-			if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
-				s->flags |= SF_REDIRECTABLE;
+		/* Try to decode HTTP request */
+		if (likely(msg->next < ci_data(chn)))
+			http_msg_analyzer(msg, &txn->hdr_idx);
 
-			if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(txn))
+		/* Still no valid request ? */
+		if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
+			if ((msg->msg_state == HTTP_MSG_ERROR) ||
+			    channel_full(chn, global.tune.maxrewrite)) {
 				return 0;
+			}
+			/* wait for final state */
+			smp->flags |= SMP_F_MAY_CHANGE;
+			return 0;
 		}
 
+		/* OK we just got a valid HTTP message. We have some minor
+		 * preparation to perform so that further checks can rely
+		 * on HTTP tests.
+		 */
+
-		if (req_vol && txn->rsp.msg_state != HTTP_MSG_RPBEFORE) {
-			return 0;  /* data might have moved and indexes changed */
+		/* If the message was parsed but was too large, we must absolutely
+		 * return an error so that it is not processed. At the moment this
+		 * cannot happen, but if the parsers are to change in the future,
+		 * we want this check to be maintained.
+		 */
+		if (unlikely(ci_head(chn) + ci_data(chn) >
+			     b_wrap(&chn->buf) - global.tune.maxrewrite)) {
+			msg->err_state = msg->msg_state;
+			msg->msg_state = HTTP_MSG_ERROR;
+			smp->data.u.sint = 1;
+			return 1;
 		}
 
-		/* otherwise everything's ready for the request */
-	}
-	else {
-		/* Check for a dependency on a response */
-		if (txn->rsp.msg_state < HTTP_MSG_BODY) {
-			smp->flags |= SMP_F_MAY_CHANGE;
+		txn->meth = find_http_meth(ci_head(chn), msg->sl.rq.m_l);
+		if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
+			s->flags |= SF_REDIRECTABLE;
+
+		if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(txn))
 			return 0;
-		}
 	}
 
+  end:
 	/* everything's OK */
 	smp->data.u.sint = 1;
 	return 1;
@@ -417,12 +410,13 @@
  */
 static int smp_fetch_meth(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	int meth;
 	struct http_txn *txn;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 
 		if (!htx)
 			return 0;
@@ -434,10 +428,10 @@
 		if (meth == HTTP_METH_OTHER) {
 			struct htx_sl *sl;
 
-			if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
+			if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) {
 				/* ensure the indexes are not affected */
 				return 0;
-
+			}
 			sl = http_find_stline(htx);
 			smp->flags |= SMP_F_CONST;
 			smp->data.u.meth.str.area = HTX_SL_REQ_MPTR(sl);
@@ -447,16 +441,17 @@
 	}
 	else {
 		/* LEGACY version */
-		CHECK_HTTP_MESSAGE_FIRST_PERM();
+		CHECK_HTTP_MESSAGE_FIRST_PERM(chn);
 
 		txn = smp->strm->txn;
 		meth = txn->meth;
 		smp->data.type = SMP_T_METH;
 		smp->data.u.meth.meth = meth;
 		if (meth == HTTP_METH_OTHER) {
-			if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
+			if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) {
 				/* ensure the indexes are not affected */
 				return 0;
+			}
 			smp->flags |= SMP_F_CONST;
 			smp->data.u.meth.str.data = txn->req.sl.rq.m_l;
 			smp->data.u.meth.str.area = ci_head(txn->req.chn);
@@ -468,13 +463,14 @@
 
 static int smp_fetch_rqver(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	char *ptr;
 	int len;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct htx_sl *sl;
 
 		if (!htx)
@@ -486,11 +482,11 @@
 	}
 	else {
 		/* LEGACY version */
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
 		len = txn->req.sl.rq.v_l;
-		ptr = ci_head(txn->req.chn) + txn->req.sl.rq.v;
+		ptr = ci_head(chn) + txn->req.sl.rq.v;
 	}
 
 	while ((len-- > 0) && (*ptr++ != '/'));
@@ -507,13 +503,14 @@
 
 static int smp_fetch_stver(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_RES_CHN(smp);
 	struct http_txn *txn;
 	char *ptr;
 	int len;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct htx_sl *sl;
 
 		if (!htx)
@@ -525,14 +522,11 @@
 	}
 	else {
 		/* LEGACY version */
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
-		if (txn->rsp.msg_state < HTTP_MSG_BODY)
-			return 0;
-
 		len = txn->rsp.sl.st.v_l;
-		ptr = ci_head(txn->rsp.chn);
+		ptr = ci_head(chn);
 	}
 
 	while ((len-- > 0) && (*ptr++ != '/'));
@@ -550,13 +544,14 @@
 /* 3. Check on Status Code. We manipulate integers here. */
 static int smp_fetch_stcode(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_RES_CHN(smp);
 	struct http_txn *txn;
 	char *ptr;
 	int len;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct htx_sl *sl;
 
 		if (!htx)
@@ -568,14 +563,11 @@
 	}
 	else {
 		/* LEGACY version */
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
-		if (txn->rsp.msg_state < HTTP_MSG_BODY)
-			return 0;
-
 		len = txn->rsp.sl.st.c_l;
-		ptr = ci_head(txn->rsp.chn) + txn->rsp.sl.st.c;
+		ptr = ci_head(chn) + txn->rsp.sl.st.c;
 	}
 
 	smp->data.type = SMP_T_SINT;
@@ -609,11 +601,12 @@
  */
 static int smp_fetch_hdrs(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct buffer *temp;
 		int32_t pos;
 
@@ -646,16 +639,16 @@
 		struct http_msg *msg;
 		struct hdr_idx *idx;
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
 		idx = &txn->hdr_idx;
 		msg = &txn->req;
 
 		smp->data.type = SMP_T_STR;
-		smp->data.u.str.area = ci_head(msg->chn) + hdr_idx_first_pos(idx);
+		smp->data.u.str.area = ci_head(chn) + hdr_idx_first_pos(idx);
 		smp->data.u.str.data = msg->eoh - hdr_idx_first_pos(idx) + 1 +
-			(ci_head(msg->chn)[msg->eoh] == '\r');
+			(ci_head(chn)[msg->eoh] == '\r');
 	}
 	return 1;
 }
@@ -675,12 +668,13 @@
  */
 static int smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	struct buffer *temp;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct buffer *temp;
 		char *p, *end;
 		int32_t pos;
@@ -741,7 +735,6 @@
 	}
 	else {
 		/* LEGACY version */
-		struct http_msg *msg;
 		struct hdr_idx *idx;
 		const char *cur_ptr, *cur_next, *p;
 		int old_idx, cur_idx;
@@ -752,7 +745,7 @@
 		char *buf;
 		char *end;
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		temp = get_trash_chunk();
 		buf = temp->area;
@@ -760,11 +753,10 @@
 
 		txn = smp->strm->txn;
 		idx = &txn->hdr_idx;
-		msg = &txn->req;
 
 		/* Build array of headers. */
 		old_idx = 0;
-		cur_next = ci_head(msg->chn) + hdr_idx_first_pos(idx);
+		cur_next = ci_head(chn) + hdr_idx_first_pos(idx);
 		while (1) {
 			cur_idx = idx->v[old_idx].next;
 			if (!cur_idx)
@@ -838,11 +830,12 @@
  */
 static int smp_fetch_body(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct buffer *temp;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		int32_t pos;
 
 		if (!htx)
@@ -872,19 +865,15 @@
 		unsigned long block1;
 		char *body;
 
-		CHECK_HTTP_MESSAGE_FIRST();
-
-		if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
-			msg = &smp->strm->txn->req;
-		else
-			msg = &smp->strm->txn->rsp;
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
+		msg = &smp->strm->txn->req;
 		len  = http_body_bytes(msg);
-		body = c_ptr(msg->chn, -http_data_rewind(msg));
+		body = c_ptr(chn, -http_data_rewind(msg));
 
 		block1 = len;
-		if (block1 > b_wrap(&msg->chn->buf) - body)
-			block1 = b_wrap(&msg->chn->buf) - body;
+		if (block1 > b_wrap(&chn->buf) - body)
+			block1 = b_wrap(&chn->buf) - body;
 
 		if (block1 == len) {
 			/* buffer is not wrapped (or empty) */
@@ -897,8 +886,7 @@
 			/* buffer is wrapped, we need to defragment it */
 			temp = get_trash_chunk();
 			memcpy(temp->area, body, block1);
-			memcpy(temp->area + block1, b_orig(&msg->chn->buf),
-			       len - block1);
+			memcpy(temp->area + block1, b_orig(&chn->buf), len - block1);
 			smp->data.type = SMP_T_BIN;
 			smp->data.u.str.area = temp->area;
 			smp->data.u.str.data = len;
@@ -914,9 +902,11 @@
  */
 static int smp_fetch_body_len(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
+
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		int32_t pos;
 		unsigned long long len = 0;
 
@@ -942,13 +932,9 @@
 		/* LEGACY version */
 		struct http_msg *msg;
 
-		CHECK_HTTP_MESSAGE_FIRST();
-
-		if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
-			msg = &smp->strm->txn->req;
-		else
-			msg = &smp->strm->txn->rsp;
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
+		msg = &smp->strm->txn->req;
 		smp->data.type = SMP_T_SINT;
 		smp->data.u.sint = http_body_bytes(msg);
 
@@ -964,9 +950,11 @@
  */
 static int smp_fetch_body_size(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
+
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		int32_t pos;
 		unsigned long long len = 0;
 
@@ -994,13 +982,9 @@
 		/* LEGACY version */
 		struct http_msg *msg;
 
-		CHECK_HTTP_MESSAGE_FIRST();
-
-		if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
-			msg = &smp->strm->txn->req;
-		else
-			msg = &smp->strm->txn->rsp;
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
+		msg = &smp->strm->txn->req;
 		smp->data.type = SMP_T_SINT;
 		smp->data.u.sint = msg->body_len;
 
@@ -1013,11 +997,12 @@
 /* 4. Check on URL/URI. A pointer to the URI is stored. */
 static int smp_fetch_url(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct htx_sl *sl;
 
 		if (!htx)
@@ -1030,12 +1015,12 @@
 	}
 	else {
 		/* LEGACY version */
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
 		smp->data.type = SMP_T_STR;
 		smp->data.u.str.data = txn->req.sl.rq.u_l;
-		smp->data.u.str.area = ci_head(txn->req.chn) + txn->req.sl.rq.u;
+		smp->data.u.str.area = ci_head(chn) + txn->req.sl.rq.u;
 		smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
 	}
 	return 1;
@@ -1043,12 +1028,13 @@
 
 static int smp_fetch_url_ip(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	struct sockaddr_storage addr;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct htx_sl *sl;
 
 		if (!htx)
@@ -1058,10 +1044,10 @@
 	}
 	else {
 		/* LEGACY version */
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
-		url2sa(ci_head(txn->req.chn) + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL);
+		url2sa(ci_head(chn) + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL);
 	}
 
 	if (((struct sockaddr_in *)&addr)->sin_family != AF_INET)
@@ -1075,12 +1061,13 @@
 
 static int smp_fetch_url_port(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct http_txn *txn;
 	struct sockaddr_storage addr;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct htx_sl *sl;
 
 		if (!htx)
@@ -1090,10 +1077,10 @@
 	}
 	else {
 		/* LEGACY version */
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
-		url2sa(ci_head(txn->req.chn) + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL);
+		url2sa(ci_head(chn) + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL);
 	}
 	if (((struct sockaddr_in *)&addr)->sin_family != AF_INET)
 		return 0;
@@ -1113,11 +1100,13 @@
  */
 static int smp_fetch_fhdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.fhdr, res.fhdr */
+	struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	int occ = 0;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct http_hdr_ctx *ctx = smp->ctx.a[0];
 		struct ist name;
 
@@ -1183,10 +1172,10 @@
 				occ = args[1].data.sint;
 		}
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		idx = &smp->strm->txn->hdr_idx;
-		msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
+		msg = (!(chn->flags & CF_ISRESP) ? &smp->strm->txn->req : &smp->strm->txn->rsp);
 
 		if (ctx && !(smp->flags & SMP_F_NOT_LAST))
 			/* search for header from the beginning */
@@ -1215,11 +1204,13 @@
  */
 static int smp_fetch_fhdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.fhdr_cnt, res.fhdr_cnt */
+	struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	int cnt;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct http_hdr_ctx ctx;
 		struct ist name;
 
@@ -1252,10 +1243,10 @@
 			len = args->data.str.data;
 		}
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		idx = &smp->strm->txn->hdr_idx;
-		msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
+		msg = (!(chn->flags & CF_ISRESP) ? &smp->strm->txn->req : &smp->strm->txn->rsp);
 
 		ctx.idx = 0;
 		cnt = 0;
@@ -1271,12 +1262,14 @@
 
 static int smp_fetch_hdr_names(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.hdr_names, res.hdr_names */
+	struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	struct buffer *temp;
 	char del = ',';
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		int32_t pos;
 
 		if (!htx)
@@ -1311,10 +1304,10 @@
 		if (args && args->type == ARGT_STR)
 			del = *args[0].data.str.area;
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		idx = &smp->strm->txn->hdr_idx;
-		msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
+		msg = (!(chn->flags & CF_ISRESP) ? &smp->strm->txn->req : &smp->strm->txn->rsp);
 
 		temp = get_trash_chunk();
 
@@ -1341,11 +1334,13 @@
  */
 static int smp_fetch_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.hdr / hdr, res.hdr / shdr */
+	struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	int occ = 0;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct http_hdr_ctx *ctx = smp->ctx.a[0];
 		struct ist name;
 
@@ -1411,10 +1406,10 @@
 				occ = args[1].data.sint;
 		}
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		idx = &smp->strm->txn->hdr_idx;
-		msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
+		msg = (!(chn->flags & CF_ISRESP) ? &smp->strm->txn->req : &smp->strm->txn->rsp);
 
 		if (ctx && !(smp->flags & SMP_F_NOT_LAST))
 			/* search for header from the beginning */
@@ -1443,11 +1438,13 @@
  */
 static int smp_fetch_hdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.hdr_cnt / hdr_cnt, res.hdr_cnt / shdr_cnt */
+	struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	int cnt;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct http_hdr_ctx ctx;
 		struct ist name;
 
@@ -1480,10 +1477,10 @@
 			len = args->data.str.data;
 		}
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		idx = &smp->strm->txn->hdr_idx;
-		msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
+		msg = (!(chn->flags & CF_ISRESP) ? &smp->strm->txn->req : &smp->strm->txn->rsp);
 
 		ctx.idx = 0;
 		cnt = 0;
@@ -1552,9 +1549,11 @@
  */
 static int smp_fetch_path(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
+
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct htx_sl *sl;
 		struct ist path;
 		size_t len;
@@ -1580,10 +1579,10 @@
 		struct http_txn *txn;
 		char *ptr, *end;
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
-		end = ci_head(txn->req.chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+		end = ci_head(chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
 		ptr = http_txn_get_path(txn);
 		if (!ptr)
 			return 0;
@@ -1610,11 +1609,12 @@
  */
 static int smp_fetch_base(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	struct buffer *temp;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct htx_sl *sl;
 		struct http_hdr_ctx ctx;
 		struct ist path;
@@ -1652,11 +1652,11 @@
 		char *ptr, *end, *beg;
 		struct hdr_ctx ctx;
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
 		ctx.idx = 0;
-		if (!http_find_header2("Host", 4, ci_head(txn->req.chn), &txn->hdr_idx, &ctx) || !ctx.vlen)
+		if (!http_find_header2("Host", 4, ci_head(chn), &txn->hdr_idx, &ctx) || !ctx.vlen)
 			return smp_fetch_path(args, smp, kw, private);
 
 		/* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */
@@ -1667,7 +1667,7 @@
 		smp->data.u.str.data = ctx.vlen;
 
 		/* now retrieve the path */
-		end = ci_head(txn->req.chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+		end = ci_head(chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
 		beg = http_txn_get_path(txn);
 		if (!beg)
 			beg = end;
@@ -1695,11 +1695,12 @@
  */
 static int smp_fetch_base32(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	unsigned int hash = 0;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct htx_sl *sl;
 		struct http_hdr_ctx ctx;
 		struct ist path;
@@ -1736,11 +1737,11 @@
 		char *ptr, *beg, *end;
 		int len;
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
 		ctx.idx = 0;
-		if (http_find_header2("Host", 4, ci_head(txn->req.chn), &txn->hdr_idx, &ctx)) {
+		if (http_find_header2("Host", 4, ci_head(chn), &txn->hdr_idx, &ctx)) {
 			/* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */
 			ptr = ctx.line + ctx.val;
 			len = ctx.vlen;
@@ -1749,7 +1750,7 @@
 		}
 
 		/* now retrieve the path */
-		end = ci_head(txn->req.chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+		end = ci_head(chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
 		beg = http_txn_get_path(txn);
 		if (!beg)
 			beg = end;
@@ -1820,11 +1821,12 @@
  */
 static int smp_fetch_query(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	char *ptr, *end;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct htx_sl *sl;
 
 		if (!htx)
@@ -1838,10 +1840,10 @@
 		/* LEGACY version */
 		struct http_txn *txn;
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
-		ptr = ci_head(txn->req.chn) + txn->req.sl.rq.u;
+		ptr = ci_head(chn) + txn->req.sl.rq.u;
 		end = ptr + txn->req.sl.rq.u_l;
 	}
 
@@ -1860,9 +1862,11 @@
 
 static int smp_fetch_proto_http(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
+
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 
 		if (!htx)
 			return 0;
@@ -1873,7 +1877,7 @@
 		/* Note: hdr_idx.v cannot be NULL in this ACL because the ACL is tagged
 		 * as a layer7 ACL, which involves automatic allocation of hdr_idx.
 		 */
-		CHECK_HTTP_MESSAGE_FIRST_PERM();
+		CHECK_HTTP_MESSAGE_FIRST_PERM(chn);
 	}
 		smp->data.type = SMP_T_BOOL;
 	smp->data.u.sint = 1;
@@ -1891,20 +1895,21 @@
 /* Accepts exactly 1 argument of type userlist */
 static int smp_fetch_http_auth(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 
 	if (!args || args->type != ARGT_USR)
 		return 0;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 
 		if (!htx)
 			return 0;
 	}
 	else {
 		/* LEGACY version */
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 	}
 
 	if (!get_http_auth(smp))
@@ -1918,19 +1923,21 @@
 /* Accepts exactly 1 argument of type userlist */
 static int smp_fetch_http_auth_grp(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
+
 	if (!args || args->type != ARGT_USR)
 		return 0;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 
 		if (!htx)
 			return 0;
 	}
 	else {
 		/* LEGACY version */
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 	}
 
 	if (!get_http_auth(smp))
@@ -2121,6 +2128,8 @@
  */
 static int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.cookie / cookie / cook, res.cookie / scook / set-cookie */
+	struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	int occ = 0;
 	int found = 0;
 
@@ -2129,7 +2138,7 @@
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct http_hdr_ctx *ctx = smp->ctx.a[2];
 		struct ist hdr;
 
@@ -2143,9 +2152,7 @@
 		if (!htx)
 			return 0;
 
-		hdr = (((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
-		       ? ist("Cookie")
-		       : ist("Set-Cookie"));
+		hdr = (!(chn->flags & CF_ISRESP) ? ist("Cookie") : ist("Set-Cookie"));
 
 		if (!occ && !(smp->opt & SMP_OPT_ITERATE))
 			/* no explicit occurrence and single fetch => last cookie by default */
@@ -2198,10 +2205,8 @@
 	}
 	else {
 		/* LEGACY version */
-		struct http_txn *txn;
 		struct hdr_idx *idx;
 		struct hdr_ctx *ctx = smp->ctx.a[2];
-		const struct http_msg *msg;
 		const char *hdr_name;
 		int hdr_name_len;
 		char *sol;
@@ -2213,17 +2218,13 @@
 			smp->ctx.a[2] = ctx;
 		}
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
-		txn = smp->strm->txn;
 		idx = &smp->strm->txn->hdr_idx;
-
-		if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
-			msg = &txn->req;
+		if (!(chn->flags & CF_ISRESP)) {
 			hdr_name = "Cookie";
 			hdr_name_len = 6;
 		} else {
-			msg = &txn->rsp;
 			hdr_name = "Set-Cookie";
 			hdr_name_len = 10;
 		}
@@ -2237,7 +2238,7 @@
 		 * next one.
 		 */
 
-		sol = ci_head(msg->chn);
+		sol = ci_head(chn);
 		if (!(smp->flags & SMP_F_NOT_LAST)) {
 			/* search for the header from the beginning, we must first initialize
 			 * the search parameters.
@@ -2293,6 +2294,8 @@
  */
 static int smp_fetch_cookie_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	/* possible keywords: req.cook_cnt / cook_cnt, res.cook_cnt / scook_cnt */
+	struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
 	char *val_beg, *val_end;
 	int cnt;
 
@@ -2301,16 +2304,14 @@
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct http_hdr_ctx ctx;
 		struct ist hdr;
 
 		if (!htx)
 			return 0;
 
-		hdr = (((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
-		       ? ist("Cookie")
-		       : ist("Set-Cookie"));
+		hdr = (!(chn->flags & CF_ISRESP) ? ist("Cookie") : ist("Set-Cookie"));
 
 		val_end = val_beg = NULL;
 		ctx.blk = NULL;
@@ -2341,30 +2342,24 @@
 	}
 	else {
 		/* LEGACY version */
-		struct http_txn *txn;
 		struct hdr_idx *idx;
 		struct hdr_ctx ctx;
-		const struct http_msg *msg;
 		const char *hdr_name;
 		int hdr_name_len;
 		char *sol;
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
-		txn = smp->strm->txn;
 		idx = &smp->strm->txn->hdr_idx;
-
-		if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
-			msg = &txn->req;
+		if (!(chn->flags & CF_ISRESP)) {
 			hdr_name = "Cookie";
 			hdr_name_len = 6;
 		} else {
-			msg = &txn->rsp;
 			hdr_name = "Set-Cookie";
 			hdr_name_len = 10;
 		}
 
-		sol = ci_head(msg->chn);
+		sol = ci_head(chn);
 		val_end = val_beg = NULL;
 		ctx.idx = 0;
 		cnt = 0;
@@ -2477,6 +2472,7 @@
  */
 static int smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	char delim = '?';
 	const char *name;
 	int name_len;
@@ -2499,7 +2495,7 @@
 	if (!smp->ctx.a[0]) { // first call, find the query string
 		if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 			/* HTX version */
-			struct htx *htx = smp_prefetch_htx(smp, args);
+			struct htx *htx = smp_prefetch_htx(smp, chn);
 			struct htx_sl *sl;
 
 			if (!htx)
@@ -2516,16 +2512,16 @@
 			/* LEGACY version */
 			struct http_msg *msg;
 
-			CHECK_HTTP_MESSAGE_FIRST();
+			CHECK_HTTP_MESSAGE_FIRST(chn);
 
 			msg = &smp->strm->txn->req;
 
-			smp->ctx.a[0] = http_find_param_list(ci_head(msg->chn) + msg->sl.rq.u,
+			smp->ctx.a[0] = http_find_param_list(ci_head(chn) + msg->sl.rq.u,
 							     msg->sl.rq.u_l, delim);
 			if (!smp->ctx.a[0])
 				return 0;
 
-			smp->ctx.a[1] = ci_head(msg->chn) + msg->sl.rq.u + msg->sl.rq.u_l;
+			smp->ctx.a[1] = ci_head(chn) + msg->sl.rq.u + msg->sl.rq.u_l;
 		}
 
 		/* Assume that the context is filled with NULL pointer
@@ -2547,6 +2543,7 @@
  */
 static int smp_fetch_body_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	const char *name;
 	int name_len;
 
@@ -2563,7 +2560,7 @@
 	if (!smp->ctx.a[0]) { // first call, find the query string
 		if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 			/* HTX version */
-			struct htx *htx = smp_prefetch_htx(smp, args);
+			struct htx *htx = smp_prefetch_htx(smp, chn);
 			struct buffer *temp;
 			int32_t pos;
 
@@ -2599,19 +2596,15 @@
 			unsigned long block1;
 			char *body;
 
-			CHECK_HTTP_MESSAGE_FIRST();
+			CHECK_HTTP_MESSAGE_FIRST(chn);
 
-			if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
-				msg = &smp->strm->txn->req;
-			else
-				msg = &smp->strm->txn->rsp;
-
+			msg = &smp->strm->txn->req;
 			len  = http_body_bytes(msg);
-			body = c_ptr(msg->chn, -http_data_rewind(msg));
+			body = c_ptr(chn, -http_data_rewind(msg));
 
 			block1 = len;
-			if (block1 > b_wrap(&msg->chn->buf) - body)
-				block1 = b_wrap(&msg->chn->buf) - body;
+			if (block1 > b_wrap(&chn->buf) - body)
+				block1 = b_wrap(&chn->buf) - body;
 
 			if (block1 == len) {
 				/* buffer is not wrapped (or empty) */
@@ -2628,8 +2621,8 @@
 				/* buffer is wrapped, we need to defragment it */
 				smp->ctx.a[0] = body;
 				smp->ctx.a[1] = body + block1;
-				smp->ctx.a[2] = b_orig(&msg->chn->buf);
-				smp->ctx.a[3] = b_orig(&msg->chn->buf) + ( len - block1 );
+				smp->ctx.a[2] = b_orig(&chn->buf);
+				smp->ctx.a[3] = b_orig(&chn->buf) + ( len - block1 );
 			}
 		}
 	}
@@ -2665,11 +2658,12 @@
  */
 static int smp_fetch_url32(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+	struct channel *chn = SMP_REQ_CHN(smp);
 	unsigned int hash = 0;
 
 	if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
 		/* HTX version */
-		struct htx *htx = smp_prefetch_htx(smp, args);
+		struct htx *htx = smp_prefetch_htx(smp, chn);
 		struct http_hdr_ctx ctx;
 		struct htx_sl *sl;
 		struct ist path;
@@ -2703,11 +2697,11 @@
 		char *ptr, *beg, *end;
 		int len;
 
-		CHECK_HTTP_MESSAGE_FIRST();
+		CHECK_HTTP_MESSAGE_FIRST(chn);
 
 		txn = smp->strm->txn;
 		ctx.idx = 0;
-		if (http_find_header2("Host", 4, ci_head(txn->req.chn), &txn->hdr_idx, &ctx)) {
+		if (http_find_header2("Host", 4, ci_head(chn), &txn->hdr_idx, &ctx)) {
 			/* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */
 			ptr = ctx.line + ctx.val;
 			len = ctx.vlen;
@@ -2716,7 +2710,7 @@
 		}
 
 		/* now retrieve the path */
-		end = ci_head(txn->req.chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+		end = ci_head(chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
 		beg = http_txn_get_path(txn);
 		if (!beg)
 			beg = end;