MEDIUM: tree-wide: Move flags about shut from the channel to the SC

The purpose of this patch is only a one-to-one replacement, as far as
possible.

CF_SHUTR(_NOW) and CF_SHUTW(_NOW) flags are now carried by the
stream-connecter. CF_ prefix is replaced by SC_FL_ one. Of course, it is not
so simple because at many places, we were testing if a channel was shut for
reads and writes in same time. To do the same, shut for reads must be tested
on one side on the SC and shut for writes on the other side on the opposite
SC. A special care was taken with process_stream(). flags of SCs must be
saved to be able to detect changes, just like for the channels.
diff --git a/src/activity.c b/src/activity.c
index 010fd31..bfdd6f3 100644
--- a/src/activity.c
+++ b/src/activity.c
@@ -624,7 +624,8 @@
 	int max_lines;
 	int i, j, max;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side ! */
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		return 1;
 
 	chunk_reset(&trash);
@@ -887,7 +888,8 @@
 	int thr, queue;
 	int i, max;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side ! */
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		return 1;
 
 	/* It's not possible to scan queues in small chunks and yield in the
@@ -1027,7 +1029,8 @@
 	struct timeval up;
 	int thr;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side ! */
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		return 1;
 
 	chunk_reset(&trash);
diff --git a/src/applet.c b/src/applet.c
index 3ed3c7a..0c5b40d 100644
--- a/src/applet.c
+++ b/src/applet.c
@@ -471,7 +471,7 @@
 	     (b_size(sc_ib(sc)) && !b_data(sc_ib(sc)) && sc->flags & SC_FL_NEED_ROOM) || // asks for room in an empty buffer
 	     (b_data(sc_ob(sc)) && sc_is_send_allowed(sc)) || // asks for data already present
 	     (!b_data(sc_ib(sc)) && b_data(sc_ob(sc)) && // didn't return anything ...
-	      (sc_oc(sc)->flags & (CF_WRITE_EVENT|CF_SHUTW_NOW)) == CF_SHUTW_NOW))) { // ... and left data pending after a shut
+	      (!(sc_oc(sc)->flags & CF_WRITE_EVENT) && (chn_cons(sc_oc(sc))->flags & SC_FL_SHUTW_NOW))))) { // ... and left data pending after a shut
 		rate = update_freq_ctr(&app->call_rate, 1);
 		if (rate >= 100000 && app->call_rate.prev_ctr) // looped like this more than 100k times over last second
 			stream_dump_and_crash(&app->obj_type, read_freq_ctr(&app->call_rate));
diff --git a/src/backend.c b/src/backend.c
index af39fef..4fa5f24 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1956,7 +1956,7 @@
 static int back_may_abort_req(struct channel *req, struct stream *s)
 {
 	return (sc_ep_test(s->scf, SE_FL_ERROR) ||
-	        ((req->flags & (CF_SHUTW_NOW|CF_SHUTW)) &&  /* empty and client aborted */
+	        ((chn_cons(req)->flags & (SC_FL_SHUTW_NOW|SC_FL_SHUTW)) &&  /* empty and client aborted */
 	         (channel_is_empty(req) || (s->be->options & PR_O_ABRT_CLOSE))));
 }
 
@@ -2246,8 +2246,8 @@
 	DBG_TRACE_ENTER(STRM_EV_STRM_PROC|STRM_EV_CS_ST, s);
 
 	/* the client might want to abort */
-	if ((rep->flags & CF_SHUTW) ||
-	    ((req->flags & CF_SHUTW_NOW) &&
+	if ((chn_cons(rep)->flags & SC_FL_SHUTW) ||
+	    ((chn_cons(req)->flags & SC_FL_SHUTW_NOW) &&
 	     (channel_is_empty(req) || (s->be->options & PR_O_ABRT_CLOSE)))) {
 		sc->flags |= SC_FL_NOLINGER;
 		sc_shutw(sc);
@@ -2470,8 +2470,8 @@
 	 */
 	if (!(req->flags & CF_WROTE_DATA)) {
 		/* client abort ? */
-		if ((rep->flags & CF_SHUTW) ||
-		    ((req->flags & CF_SHUTW_NOW) &&
+		if ((chn_cons(rep)->flags & SC_FL_SHUTW) ||
+		    ((chn_cons(req)->flags & SC_FL_SHUTW_NOW) &&
 		     (channel_is_empty(req) || (s->be->options & PR_O_ABRT_CLOSE)))) {
 			/* give up */
 			sc->flags |= SC_FL_NOLINGER;
diff --git a/src/channel.c b/src/channel.c
index 68360d9..70c7931 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -206,8 +206,8 @@
 	max = len;
 
 	/* closed or empty + imminent close = -1; empty = 0 */
-	if (unlikely((chn->flags & CF_SHUTW) || channel_is_empty(chn))) {
-		if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
+	if (unlikely((chn_cons(chn)->flags & SC_FL_SHUTW) || channel_is_empty(chn))) {
+		if (chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW))
 			ret = -1;
 		goto out;
 	}
@@ -252,7 +252,7 @@
 	if (ret > 0 && ret < len &&
 	    (ret < co_data(chn) || channel_may_recv(chn)) &&
 	    !found &&
-	    !(chn->flags & (CF_SHUTW|CF_SHUTW_NOW)))
+	    !(chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)))
 		ret = 0;
  out:
 	if (max)
@@ -279,8 +279,8 @@
 	max = len;
 
 	/* closed or empty + imminent close = -1; empty = 0 */
-	if (unlikely((chn->flags & CF_SHUTW) || channel_is_empty(chn))) {
-		if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
+	if (unlikely((chn_cons(chn)->flags & SC_FL_SHUTW) || channel_is_empty(chn))) {
+		if (chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW))
 			ret = -1;
 		goto out;
 	}
@@ -303,7 +303,7 @@
 	if (ret > 0 && ret < len &&
 	    (ret < co_data(chn) || channel_may_recv(chn)) &&
 	    *(str-1) != sep &&
-	    !(chn->flags & (CF_SHUTW|CF_SHUTW_NOW)))
+	    !(chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)))
 		ret = 0;
  out:
 	if (max)
@@ -330,8 +330,8 @@
 	max = len;
 
 	/* closed or empty + imminent close = -1; empty = 0 */
-	if (unlikely((chn->flags & CF_SHUTW) || channel_is_empty(chn))) {
-		if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
+	if (unlikely((chn_cons(chn)->flags & SC_FL_SHUTW) || channel_is_empty(chn))) {
+		if (chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW))
 			ret = -1;
 		goto out;
 	}
@@ -354,7 +354,7 @@
 	if (ret > 0 && ret < len &&
 	    (ret < co_data(chn) || channel_may_recv(chn)) &&
 	    *(str-1) != '\n' &&
-	    !(chn->flags & (CF_SHUTW|CF_SHUTW_NOW)))
+	    !(chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)))
 		ret = 0;
  out:
 	if (max)
@@ -372,11 +372,11 @@
  */
 int co_getchar(const struct channel *chn, char *c)
 {
-	if (chn->flags & CF_SHUTW)
+	if (chn_cons(chn)->flags & SC_FL_SHUTW)
 		return -1;
 
 	if (unlikely(co_data(chn) == 0)) {
-		if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
+		if (chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW))
 			return -1;
 		return 0;
 	}
@@ -395,11 +395,11 @@
  */
 int co_getblk(const struct channel *chn, char *blk, int len, int offset)
 {
-	if (chn->flags & CF_SHUTW)
+	if (chn_cons(chn)->flags & SC_FL_SHUTW)
 		return -1;
 
 	if (len + offset > co_data(chn) || co_data(chn) == 0) {
-		if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
+		if (chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW))
 			return -1;
 		return 0;
 	}
@@ -418,7 +418,7 @@
 int co_getblk_nc(const struct channel *chn, const char **blk1, size_t *len1, const char **blk2, size_t *len2)
 {
 	if (unlikely(co_data(chn) == 0)) {
-		if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW))
+		if (chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW))
 			return -1;
 		return 0;
 	}
@@ -460,7 +460,7 @@
 		}
 	}
 
-	if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
+	if (chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) {
 		/* If we have found no LF and the buffer is shut, then
 		 * the resulting string is made of the concatenation of
 		 * the pending blocks (1 or 2).
@@ -484,7 +484,7 @@
                  char **blk2, size_t *len2)
 {
 	if (unlikely(ci_data(chn) == 0)) {
-		if (chn->flags & CF_SHUTR)
+		if (chn_prod(chn)->flags & SC_FL_SHUTR)
 			return -1;
 		return 0;
 	}
@@ -536,7 +536,7 @@
 		}
 	}
 
-	if (chn->flags & CF_SHUTW) {
+	if (chn_cons(chn)->flags & SC_FL_SHUTW) {
 		/* If we have found no LF and the buffer is shut, then
 		 * the resulting string is made of the concatenation of
 		 * the pending blocks (1 or 2).
diff --git a/src/cli.c b/src/cli.c
index 1c942e1..21d6620 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -1213,7 +1213,8 @@
 	struct stconn *sc = appctx_sc(appctx);
 	char **var = ctx->var;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		return 1;
 
 	chunk_reset(&trash);
@@ -1251,7 +1252,8 @@
 	int fd = fdctx->fd;
 	int ret = 1;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		goto end;
 
 	chunk_reset(&trash);
@@ -2685,7 +2687,7 @@
 	goto read_again;
 
 missing_data:
-        if (req->flags & CF_SHUTR) {
+        if (chn_prod(req)->flags & SC_FL_SHUTR) {
                 /* There is no more request or a only a partial one and we
                  * receive a close from the client, we can leave */
                 channel_shutw_now(&s->res);
@@ -2709,7 +2711,7 @@
 	struct proxy *be = s->be;
 
 	if (sc_ep_test(s->scb, SE_FL_ERR_PENDING|SE_FL_ERROR) || (rep->flags & (CF_READ_TIMEOUT|CF_WRITE_TIMEOUT)) ||
-	    ((rep->flags & CF_SHUTW) && (rep->to_forward || co_data(rep)))) {
+	    ((chn_cons(rep)->flags & SC_FL_SHUTW) && (rep->to_forward || co_data(rep)))) {
 		pcli_reply_and_close(s, "Can't connect to the target CLI!\n");
 		s->req.analysers &= ~AN_REQ_WAIT_CLI;
 		s->res.analysers &= ~AN_RES_WAIT_CLI;
@@ -2734,7 +2736,7 @@
 		return 0;
 	}
 
-	if (rep->flags & CF_SHUTR) {
+	if (chn_prod(rep)->flags & SC_FL_SHUTR) {
 		/* stream cleanup */
 
 		pcli_write_prompt(s);
@@ -2823,9 +2825,11 @@
 		sockaddr_free(&s->scb->dst);
 
 		sc_set_state(s->scb, SC_ST_INI);
+		s->scb->flags &= ~(SC_FL_SHUTW|SC_FL_SHUTW_NOW);
 		s->scb->flags &= SC_FL_ISBACK | SC_FL_DONT_WAKE; /* we're in the context of process_stream */
-		s->req.flags &= ~(CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CONNECT|CF_STREAMER|CF_STREAMER_FAST|CF_WROTE_DATA);
-		s->res.flags &= ~(CF_SHUTR|CF_SHUTR_NOW|CF_STREAMER|CF_STREAMER_FAST|CF_WRITE_EVENT|CF_WROTE_DATA|CF_READ_EVENT);
+
+		s->req.flags &= ~(CF_AUTO_CONNECT|CF_STREAMER|CF_STREAMER_FAST|CF_WROTE_DATA);
+		s->res.flags &= ~(CF_STREAMER|CF_STREAMER_FAST|CF_WRITE_EVENT|CF_WROTE_DATA|CF_READ_EVENT);
 		s->flags &= ~(SF_DIRECT|SF_ASSIGNED|SF_BE_ASSIGNED|SF_FORCE_PRST|SF_IGNORE_PRST);
 		s->flags &= ~(SF_CURR_SESS|SF_REDIRECTABLE|SF_SRV_REUSED);
 		s->flags &= ~(SF_ERR_MASK|SF_FINST_MASK|SF_REDISP);
@@ -2846,6 +2850,7 @@
 		s->store_count = 0;
 		s->uniq_id = global.req_count++;
 
+		s->scf->flags &= ~(SC_FL_SHUTR|SC_FL_SHUTR_NOW);
 		s->scf->flags &= ~SC_FL_SND_NEVERWAIT;
 		s->scf->flags |= SC_FL_RCV_ONCE; /* one read is usually enough */
 
diff --git a/src/debug.c b/src/debug.c
index 3d159fd..5209166 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -313,7 +313,8 @@
 	struct stconn *sc = appctx_sc(appctx);
 	int thr;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		return 1;
 
 	if (appctx->st0)
@@ -1160,7 +1161,8 @@
 	int ret = 1;
 	int i, fd;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		goto end;
 
 	chunk_reset(&trash);
@@ -1367,7 +1369,8 @@
 	const char *pfx = ctx->match;
 	int ret = 1;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		goto end;
 
 	if (!ctx->width) {
diff --git a/src/filters.c b/src/filters.c
index 92aeb3a..7bd52ee 100644
--- a/src/filters.c
+++ b/src/filters.c
@@ -1016,11 +1016,11 @@
 	 *  - the input in closed and no data is pending
 	 *  - There is a READ/WRITE timeout
 	 */
-	if (chn->flags & CF_SHUTW) {
+	if (chn_cons(chn)->flags & SC_FL_SHUTW) {
 		ret = 1;
 		goto end;
 	}
-	if (chn->flags & CF_SHUTR) {
+	if (chn_prod(chn)->flags & SC_FL_SHUTR) {
 		if (((s->flags & SF_HTX) && htx_is_empty(htxbuf(&chn->buf))) || c_empty(chn)) {
 			ret = 1;
 			goto end;
diff --git a/src/flt_bwlim.c b/src/flt_bwlim.c
index e14b774..52fffd5 100644
--- a/src/flt_bwlim.c
+++ b/src/flt_bwlim.c
@@ -151,7 +151,7 @@
 		 */
 		ret = tokens;
 		if (tokens < conf->min_size) {
-			ret = ((chn_prod(chn)->flags & SC_FL_EOI) || (chn->flags & CF_SHUTR))
+			ret = (chn_prod(chn)->flags & (SC_FL_EOI|SC_FL_SHUTR))
 				? MIN(len, conf->min_size)
 				: conf->min_size;
 
diff --git a/src/http_ana.c b/src/http_ana.c
index 6a309c7..e319a28 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -114,7 +114,7 @@
 		/* A SHUTR at this stage means we are performing a "destructive"
 		 * HTTP upgrade (TCP>H2). In this case, we can leave.
 		 */
-		if (req->flags & CF_SHUTR) {
+		if (chn_prod(req)->flags & SC_FL_SHUTR) {
 			s->logs.logwait = 0;
                         s->logs.level = 0;
 			channel_abort(&s->req);
@@ -767,7 +767,7 @@
 	 * there and that the timeout has not expired.
 	 */
 	channel_dont_connect(req);
-	if (!(req->flags & CF_SHUTR) &&
+	if (!(chn_prod(req)->flags & SC_FL_SHUTR) &&
 	    !tick_is_expired(req->analyse_exp, now_ms)) {
 		/* Be sure to drain all data from the request channel */
 		channel_htx_erase(req, htxbuf(&req->buf));
@@ -985,7 +985,7 @@
 	if (!(txn->flags & TX_CON_WANT_TUN))
 		channel_dont_close(req);
 
-	if ((req->flags & CF_SHUTW) && co_data(req)) {
+	if ((chn_cons(req)->flags & SC_FL_SHUTW) && co_data(req)) {
 		/* request errors are most likely due to the server aborting the
 		 * transfer. */
 		goto return_srv_abort;
@@ -1004,7 +1004,7 @@
 	 * it can be abused to exhaust source ports. */
 	if (s->be->options & PR_O_ABRT_CLOSE) {
 		channel_auto_read(req);
-		if ((req->flags & CF_SHUTR) && !(txn->flags & TX_CON_WANT_TUN))
+		if ((chn_prod(req)->flags & SC_FL_SHUTR) && !(txn->flags & TX_CON_WANT_TUN))
 			s->scb->flags |= SC_FL_NOLINGER;
 		channel_auto_close(req);
 	}
@@ -1020,12 +1020,12 @@
 
  missing_data_or_waiting:
 	/* stop waiting for data if the input is closed before the end */
-	if (msg->msg_state < HTTP_MSG_ENDING && req->flags & CF_SHUTR)
+	if (msg->msg_state < HTTP_MSG_ENDING && chn_prod(req)->flags & SC_FL_SHUTR)
 		goto return_cli_abort;
 
  waiting:
 	/* waiting for the last bits to leave the buffer */
-	if (req->flags & CF_SHUTW)
+	if (chn_cons(req)->flags & SC_FL_SHUTW)
 		goto return_srv_abort;
 
 	/* When TE: chunked is used, we need to get there again to parse remaining
@@ -1132,9 +1132,11 @@
 
 	req = &s->req;
 	res = &s->res;
+
 	/* Remove any write error from the request, and read error from the response */
-	req->flags &= ~(CF_WRITE_TIMEOUT | CF_SHUTW | CF_SHUTW_NOW);
-	res->flags &= ~(CF_READ_TIMEOUT | CF_SHUTR | CF_READ_EVENT | CF_SHUTR_NOW);
+	s->scf->flags &= ~(SC_FL_SHUTR|SC_FL_SHUTR_NOW);
+	req->flags &= ~CF_WRITE_TIMEOUT;
+	res->flags &= ~(CF_READ_TIMEOUT | CF_READ_EVENT);
 	res->analysers &= AN_RES_FLT_END;
 	s->conn_err_type = STRM_ET_NONE;
 	s->flags &= ~(SF_CONN_EXP | SF_ERR_MASK | SF_FINST_MASK);
@@ -1144,6 +1146,7 @@
 	res->analyse_exp = TICK_ETERNITY;
 	res->total = 0;
 
+	s->scb->flags &= ~(SC_FL_SHUTW|SC_FL_SHUTW_NOW);
 	if (sc_reset_endp(s->scb) < 0) {
 		if (!(s->flags & SF_ERR_MASK))
 			s->flags |= SF_ERR_INTERNAL;
@@ -1293,7 +1296,9 @@
 		}
 
 		/* 3: client abort with an abortonclose */
-		else if ((rep->flags & CF_SHUTR) && ((s->req.flags & (CF_SHUTR|CF_SHUTW)) == (CF_SHUTR|CF_SHUTW))) {
+		else if ((chn_prod(rep)->flags & SC_FL_SHUTR) &&
+			 (chn_prod(&s->req)->flags & SC_FL_SHUTR) &&
+			 (chn_cons(&s->req)->flags & SC_FL_SHUTW)) {
 			_HA_ATOMIC_INC(&sess->fe->fe_counters.cli_aborts);
 			_HA_ATOMIC_INC(&s->be->be_counters.cli_aborts);
 			if (sess->listener && sess->listener->counters)
@@ -1315,7 +1320,7 @@
 		}
 
 		/* 4: close from server, capture the response if the server has started to respond */
-		else if (rep->flags & CF_SHUTR) {
+		else if (chn_prod(rep)->flags & SC_FL_SHUTR) {
 			if ((txn->flags & TX_L7_RETRY) &&
 			    (s->be->retry_type & PR_RE_DISCONNECTED)) {
 				if (co_data(rep) || do_l7_retry(s, s->scb) == 0) {
@@ -2057,7 +2062,7 @@
 	if (htx->data != co_data(res))
 		goto missing_data_or_waiting;
 
-	if (!(msg->flags & HTTP_MSGF_XFER_LEN) && res->flags & CF_SHUTR) {
+	if (!(msg->flags & HTTP_MSGF_XFER_LEN) && (chn_prod(res)->flags & SC_FL_SHUTR)) {
 		msg->msg_state = HTTP_MSG_ENDING;
 		goto ending;
 	}
@@ -2101,7 +2106,7 @@
 
 	channel_dont_close(res);
 
-	if ((res->flags & CF_SHUTW) && co_data(res)) {
+	if ((chn_cons(res)->flags & SC_FL_SHUTW) && co_data(res)) {
 		/* response errors are most likely due to the client aborting
 		 * the transfer. */
 		goto return_cli_abort;
@@ -2117,7 +2122,7 @@
 	return 0;
 
   missing_data_or_waiting:
-	if (res->flags & CF_SHUTW)
+	if (chn_cons(res)->flags & SC_FL_SHUTW)
 		goto return_cli_abort;
 
 	/* stop waiting for data if the input is closed before the end. If the
@@ -2125,8 +2130,9 @@
 	 * so we don't want to count this as a server abort. Otherwise it's a
 	 * server abort.
 	 */
-	if (msg->msg_state < HTTP_MSG_ENDING && res->flags & CF_SHUTR) {
-		if ((s->req.flags & (CF_SHUTR|CF_SHUTW)) == (CF_SHUTR|CF_SHUTW))
+	if (msg->msg_state < HTTP_MSG_ENDING && (chn_prod(res)->flags & SC_FL_SHUTR)) {
+		if ((chn_prod(&s->req)->flags & SC_FL_SHUTR) &&
+		    (chn_cons(&s->req)->flags & SC_FL_SHUTW))
 			goto return_cli_abort;
 		/* If we have some pending data, we continue the processing */
 		if (htx_is_empty(htx))
@@ -2670,7 +2676,7 @@
 		/* Always call the action function if defined */
 		if (rule->action_ptr) {
 			if (sc_ep_test(s->scf, SE_FL_ERROR) ||
-			    ((s->req.flags & CF_SHUTR) &&
+			    ((chn_prod(&s->req)->flags & SC_FL_SHUTR) &&
 			     (px->options & PR_O_ABRT_CLOSE)))
 				act_opts |= ACT_OPT_FINAL;
 
@@ -2833,7 +2839,7 @@
 		/* Always call the action function if defined */
 		if (rule->action_ptr) {
 			if (sc_ep_test(s->scf, SE_FL_ERROR) ||
-			    ((s->req.flags & CF_SHUTR) &&
+			    ((chn_prod(&s->req)->flags & SC_FL_SHUTR) &&
 			     (px->options & PR_O_ABRT_CLOSE)))
 				act_opts |= ACT_OPT_FINAL;
 
@@ -4081,7 +4087,7 @@
 	}
 
 	/* we get here if we need to wait for more data */
-	if (!(chn->flags & CF_SHUTR)) {
+	if (!(chn_prod(chn)->flags & SC_FL_SHUTR)) {
 		if (!tick_isset(chn->analyse_exp))
 			chn->analyse_exp = tick_add_ifset(now_ms, time);
 		ret = HTTP_RULE_RES_YIELD;
@@ -4270,7 +4276,7 @@
 			    txn->rsp.msg_state != HTTP_MSG_CLOSED)
 				goto check_channel_flags;
 
-			if (!(chn->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
+			if (!(chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW))) {
 				channel_shutr_now(chn);
 				channel_shutw_now(chn);
 			}
@@ -4304,7 +4310,7 @@
 
   check_channel_flags:
 	/* Here, we are in HTTP_MSG_DONE or HTTP_MSG_TUNNEL */
-	if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
+	if (chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) {
 		/* if we've just closed an output, let's switch */
 		txn->req.msg_state = HTTP_MSG_CLOSING;
 		goto http_msg_closing;
@@ -4369,7 +4375,7 @@
 			/* we're not expecting any new data to come for this
 			 * transaction, so we can close it.
 			 */
-			if (!(chn->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
+			if (!(chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW))) {
 				channel_shutr_now(chn);
 				channel_shutw_now(chn);
 			}
@@ -4400,7 +4406,7 @@
 
   check_channel_flags:
 	/* Here, we are in HTTP_MSG_DONE or HTTP_MSG_TUNNEL */
-	if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
+	if (chn_cons(chn)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) {
 		/* if we've just closed an output, let's switch */
 		txn->rsp.msg_state = HTTP_MSG_CLOSING;
 		goto http_msg_closing;
diff --git a/src/http_fetch.c b/src/http_fetch.c
index 2485eb3..d75541d 100644
--- a/src/http_fetch.c
+++ b/src/http_fetch.c
@@ -615,7 +615,7 @@
 	smp->flags = SMP_F_VOL_TEST;
 
 	if (!finished && (check || (chn && !channel_full(chn, global.tune.maxrewrite) &&
-				    !(chn_prod(chn)->flags & SC_FL_EOI) && !(chn->flags & CF_SHUTR))))
+				    !(chn_prod(chn)->flags & (SC_FL_EOI|SC_FL_SHUTR)))))
 		smp->flags |= SMP_F_MAY_CHANGE;
 
 	return 1;
diff --git a/src/map.c b/src/map.c
index 9713041..ff6bbf5 100644
--- a/src/map.c
+++ b/src/map.c
@@ -348,7 +348,8 @@
 	struct stconn *sc = appctx_sc(appctx);
 	struct pat_ref_elt *elt;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW)) {
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW)) {
 		/* If we're forced to shut down, we might have to remove our
 		 * reference to the last ref_elt being dumped.
 		 */
diff --git a/src/mworker.c b/src/mworker.c
index d71bf61..17609f6 100644
--- a/src/mworker.c
+++ b/src/mworker.c
@@ -567,7 +567,8 @@
 	char *uptime = NULL;
 	char *reloadtxt = NULL;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		return 1;
 
 	if (up < 0) /* must never be negative because of clock drift */
@@ -713,10 +714,10 @@
 	if (!cli_has_level(appctx, ACCESS_LVL_OPER))
 		return 1;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		return 1;
 
-
 	env = getenv("HAPROXY_LOAD_SUCCESS");
 	if (!env)
 		return 1;
diff --git a/src/proxy.c b/src/proxy.c
index f08ec93..276228c 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -3208,7 +3208,8 @@
 	struct stconn *sc = appctx_sc(appctx);
 	extern const char *monthname[12];
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		return 1;
 
 	chunk_reset(&trash);
diff --git a/src/quic_conn.c b/src/quic_conn.c
index 42abe07..a09216e 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -8261,7 +8261,8 @@
 	if (ctx->thr >= global.nbthread)
 		goto done;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW)) {
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW)) {
 		/* If we're forced to shut down, we might have to remove our
 		 * reference to the last stream being dumped.
 		 */
diff --git a/src/ring.c b/src/ring.c
index c3baf88..6d75f05 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -349,7 +349,8 @@
 	size_t len, cnt;
 	int ret;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		return 1;
 
 	HA_RWLOCK_WRLOCK(LOGSRV_LOCK, &ring->lock);
@@ -422,7 +423,7 @@
 		/* we've drained everything and are configured to wait for more
 		 * data or an event (keypress, close)
 		 */
-		if (!sc_oc(sc)->output && !(sc_oc(sc)->flags & CF_SHUTW)) {
+		if (!sc_oc(sc)->output && !(chn_cons(sc_oc(sc))->flags & SC_FL_SHUTW)) {
 			/* let's be woken up once new data arrive */
 			HA_RWLOCK_WRLOCK(LOGSRV_LOCK, &ring->lock);
 			LIST_APPEND(&ring->waiters, &appctx->wait_entry);
diff --git a/src/ssl_ckch.c b/src/ssl_ckch.c
index 6342787..bfb4235 100644
--- a/src/ssl_ckch.c
+++ b/src/ssl_ckch.c
@@ -2148,7 +2148,8 @@
 	struct ckch_store *old_ckchs, *new_ckchs = NULL;
 	struct ckch_inst *ckchi;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		goto end;
 
 	while (1) {
@@ -2824,7 +2825,8 @@
 	struct ckch_inst_link *ckchi_link;
 	char *path;
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		goto end;
 
 	/* The ctx was already validated by the ca-file/crl-file parsing
diff --git a/src/ssl_crtlist.c b/src/ssl_crtlist.c
index c31714d..7b410e9 100644
--- a/src/ssl_crtlist.c
+++ b/src/ssl_crtlist.c
@@ -1116,7 +1116,8 @@
 	/* for each bind_conf which use the crt-list, a new ckch_inst must be
 	 * created.
 	 */
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW))
 		goto end;
 
 	switch (ctx->state) {
diff --git a/src/stats.c b/src/stats.c
index 5ce61ee..1f80509 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -4473,7 +4473,7 @@
 	if (appctx->st0 == STAT_HTTP_POST) {
 		if (stats_process_http_post(sc))
 			appctx->st0 = STAT_HTTP_LAST;
-		else if (req->flags & CF_SHUTR)
+		else if (chn_prod(req)->flags & SC_FL_SHUTR)
 			appctx->st0 = STAT_HTTP_DONE;
 	}
 
diff --git a/src/stconn.c b/src/stconn.c
index d1aa1b5..5d94646 100644
--- a/src/stconn.c
+++ b/src/stconn.c
@@ -501,13 +501,13 @@
 
 /* Conditionally forward the close to the write side. It return 1 if it can be
  * forwarded. It is the caller responsibility to forward the close to the write
- * side. Otherwise, 0 is returned. In this case, CF_SHUTW_NOW flag may be set on
- * the channel if we are only waiting for the outgoing data to be flushed.
+ * side. Otherwise, 0 is returned. In this case, SC_FL_SHUTW_NOW flag may be set on
+ * the consumer SC if we are only waiting for the outgoing data to be flushed.
  */
 static inline int sc_cond_forward_shutw(struct stconn *sc)
 {
 	/* The close must not be forwarded */
-	if (!(sc_ic(sc)->flags & CF_SHUTR) || !(sc->flags & SC_FL_NOHALF))
+	if (!(chn_prod(sc_ic(sc))->flags & SC_FL_SHUTR) || !(sc->flags & SC_FL_NOHALF))
 		return 0;
 
 	if (!channel_is_empty(sc_ic(sc))) {
@@ -534,15 +534,17 @@
 {
 	struct channel *ic = sc_ic(sc);
 
-	if (ic->flags & CF_SHUTR)
+	if (chn_prod(ic)->flags & SC_FL_SHUTR)
 		return;
-	ic->flags |= CF_SHUTR|CF_READ_EVENT;
+
+	chn_prod(ic)->flags |= SC_FL_SHUTR;
+	ic->flags |= CF_READ_EVENT;
 	sc_ep_report_read_activity(sc);
 
 	if (!sc_state_in(sc->state, SC_SB_CON|SC_SB_RDY|SC_SB_EST))
 		return;
 
-	if (sc_oc(sc)->flags & CF_SHUTW) {
+	if (chn_cons(sc_oc(sc))->flags & SC_FL_SHUTW) {
 		sc->state = SC_ST_DIS;
 		if (sc->flags & SC_FL_ISBACK)
 			__sc_strm(sc)->conn_exp = TICK_ETERNITY;
@@ -567,10 +569,11 @@
 	struct channel *ic = sc_ic(sc);
 	struct channel *oc = sc_oc(sc);
 
-	oc->flags &= ~CF_SHUTW_NOW;
-	if (oc->flags & CF_SHUTW)
+	chn_cons(oc)->flags &= ~SC_FL_SHUTW_NOW;
+	if (chn_cons(oc)->flags & SC_FL_SHUTW)
 		return;
-	oc->flags |= CF_SHUTW|CF_WRITE_EVENT;
+	chn_cons(oc)->flags |= SC_FL_SHUTW;
+	oc->flags |= CF_WRITE_EVENT;
 	sc_set_hcto(sc);
 
 	switch (sc->state) {
@@ -583,7 +586,7 @@
 		 * no risk so we close both sides immediately.
 		 */
 		if (!sc_ep_test(sc, SE_FL_ERROR) && !(sc->flags & SC_FL_NOLINGER) &&
-		    !(ic->flags & (CF_SHUTR|CF_DONT_READ)))
+		    !(chn_prod(ic)->flags & SC_FL_SHUTR) && !(ic->flags & CF_DONT_READ))
 			return;
 
 		__fallthrough;
@@ -596,7 +599,7 @@
 		__fallthrough;
 	default:
 		sc->flags &= ~SC_FL_NOLINGER;
-		ic->flags |= CF_SHUTR;
+		chn_prod(ic)->flags |= SC_FL_SHUTR;
 		if (sc->flags & SC_FL_ISBACK)
 			__sc_strm(sc)->conn_exp = TICK_ETERNITY;
 	}
@@ -627,7 +630,7 @@
 {
 	struct channel *oc = sc_oc(sc);
 
-	if (unlikely(sc->state != SC_ST_EST || (oc->flags & CF_SHUTW)))
+	if (unlikely(sc->state != SC_ST_EST || (chn_cons(oc)->flags & SC_FL_SHUTW)))
 		return;
 
 	if (!sc_ep_test(sc, SE_FL_WAIT_DATA) ||  /* not waiting for data */
@@ -658,14 +661,15 @@
 
 	BUG_ON(!sc_conn(sc));
 
-	if (ic->flags & CF_SHUTR)
+	if (chn_prod(ic)->flags & SC_FL_SHUTR)
 		return;
-	ic->flags |= CF_SHUTR|CF_READ_EVENT;
+	chn_prod(ic)->flags |= SC_FL_SHUTR;
+	ic->flags |= CF_READ_EVENT;
 
 	if (!sc_state_in(sc->state, SC_SB_CON|SC_SB_RDY|SC_SB_EST))
 		return;
 
-	if (sc_oc(sc)->flags & CF_SHUTW) {
+	if (chn_cons(sc_oc(sc))->flags & SC_FL_SHUTW) {
 		sc_conn_shut(sc);
 		sc->state = SC_ST_DIS;
 		if (sc->flags & SC_FL_ISBACK)
@@ -690,10 +694,11 @@
 
 	BUG_ON(!sc_conn(sc));
 
-	oc->flags &= ~CF_SHUTW_NOW;
-	if (oc->flags & CF_SHUTW)
+	chn_cons(oc)->flags &= ~SC_FL_SHUTW_NOW;
+	if (chn_cons(oc)->flags & SC_FL_SHUTW)
 		return;
-	oc->flags |= CF_SHUTW|CF_WRITE_EVENT;
+	chn_cons(oc)->flags |= SC_FL_SHUTW;
+	oc->flags |= CF_WRITE_EVENT;
 	sc_set_hcto(sc);
 
 	switch (sc->state) {
@@ -726,7 +731,7 @@
 			 */
 			sc_conn_shutw(sc, CO_SHW_NORMAL);
 
-			if (!(ic->flags & (CF_SHUTR|CF_DONT_READ)))
+			if (!(chn_prod(ic)->flags & SC_FL_SHUTR) && !(ic->flags & CF_DONT_READ))
 				return;
 		}
 
@@ -744,7 +749,7 @@
 		__fallthrough;
 	default:
 		sc->flags &= ~SC_FL_NOLINGER;
-		ic->flags |= CF_SHUTR;
+		chn_prod(ic)->flags |= SC_FL_SHUTR;
 		if (sc->flags & SC_FL_ISBACK)
 			__sc_strm(sc)->conn_exp = TICK_ETERNITY;
 	}
@@ -778,7 +783,7 @@
 	BUG_ON(!sc_conn(sc));
 
 	if (unlikely(!sc_state_in(sc->state, SC_SB_RDY|SC_SB_EST) ||
-	    (oc->flags & CF_SHUTW)))
+		     (chn_cons(oc)->flags & SC_FL_SHUTW)))
 		return;
 
 	if (unlikely(channel_is_empty(oc)))  /* called with nothing to send ! */
@@ -807,14 +812,14 @@
 		 * ->o limit was reached. Maybe we just wrote the last
 		 * chunk and need to close.
 		 */
-		if (((oc->flags & (CF_SHUTW|CF_AUTO_CLOSE|CF_SHUTW_NOW)) ==
-		     (CF_AUTO_CLOSE|CF_SHUTW_NOW)) &&
+		if ((oc->flags & CF_AUTO_CLOSE) &&
+		    ((chn_cons(oc)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) == SC_FL_SHUTW_NOW) &&
 		    sc_state_in(sc->state, SC_SB_RDY|SC_SB_EST)) {
 			sc_shutw(sc);
 			goto out_wakeup;
 		}
 
-		if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == 0)
+		if ((chn_cons(oc)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) == 0)
 			sc_ep_set(sc, SE_FL_WAIT_DATA);
 	}
 	else {
@@ -827,7 +832,7 @@
 	/* in case of special condition (error, shutdown, end of write...), we
 	 * have to notify the task.
 	 */
-	if (likely((oc->flags & CF_SHUTW) ||
+	if (likely((chn_cons(oc)->flags & SC_FL_SHUTW) ||
 		   ((oc->flags & CF_WRITE_EVENT) && sc->state < SC_ST_EST) ||
 		   ((oc->flags & CF_WAKE_WRITE) &&
 		    ((channel_is_empty(oc) && !oc->to_forward) ||
@@ -852,16 +857,17 @@
 
 	BUG_ON(!sc_appctx(sc));
 
-	if (ic->flags & CF_SHUTR)
+	if (chn_prod(ic)->flags & SC_FL_SHUTR)
 		return;
-	ic->flags |= CF_SHUTR|CF_READ_EVENT;
+	chn_prod(ic)->flags |= SC_FL_SHUTR;
+	ic->flags |= CF_READ_EVENT;
 
 	/* Note: on shutr, we don't call the applet */
 
 	if (!sc_state_in(sc->state, SC_SB_CON|SC_SB_RDY|SC_SB_EST))
 		return;
 
-	if (sc_oc(sc)->flags & CF_SHUTW) {
+	if (chn_cons(sc_oc(sc))->flags & SC_FL_SHUTW) {
 		appctx_shut(__sc_appctx(sc));
 		sc->state = SC_ST_DIS;
 		if (sc->flags & SC_FL_ISBACK)
@@ -885,10 +891,11 @@
 
 	BUG_ON(!sc_appctx(sc));
 
-	oc->flags &= ~CF_SHUTW_NOW;
-	if (oc->flags & CF_SHUTW)
+	chn_cons(oc)->flags &= ~SC_FL_SHUTW_NOW;
+	if (chn_cons(oc)->flags & SC_FL_SHUTW)
 		return;
-	oc->flags |= CF_SHUTW|CF_WRITE_EVENT;
+	chn_cons(oc)->flags |= SC_FL_SHUTW;
+	oc->flags |= CF_WRITE_EVENT;
 	sc_set_hcto(sc);
 
 	/* on shutw we always wake the applet up */
@@ -904,7 +911,8 @@
 		 * no risk so we close both sides immediately.
 		 */
 		if (!sc_ep_test(sc, SE_FL_ERROR) && !(sc->flags & SC_FL_NOLINGER) &&
-		    !(ic->flags & (CF_SHUTR|CF_DONT_READ)))
+		    !(chn_prod(ic)->flags & SC_FL_SHUTR) &&
+		    !(ic->flags & CF_DONT_READ))
 			return;
 
 		__fallthrough;
@@ -918,7 +926,7 @@
 		__fallthrough;
 	default:
 		sc->flags &= ~SC_FL_NOLINGER;
-		ic->flags |= CF_SHUTR;
+		chn_prod(ic)->flags |= SC_FL_SHUTR;
 		if (sc->flags & SC_FL_ISBACK)
 			__sc_strm(sc)->conn_exp = TICK_ETERNITY;
 	}
@@ -944,7 +952,7 @@
 
 	BUG_ON(!sc_appctx(sc));
 
-	if (unlikely(sc->state != SC_ST_EST || (oc->flags & CF_SHUTW)))
+	if (unlikely(sc->state != SC_ST_EST || (chn_cons(oc)->flags & SC_FL_SHUTW)))
 		return;
 
 	/* we only wake the applet up if it was waiting for some data  and is ready to consume it */
@@ -971,7 +979,7 @@
 {
 	struct channel *ic = sc_ic(sc);
 
-	if (ic->flags & CF_SHUTR)
+	if (chn_prod(ic)->flags & SC_FL_SHUTR)
 		return;
 
 	/* Read not closed, update FD status and timeout for reads */
@@ -996,14 +1004,14 @@
 {
 	struct channel *oc = sc_oc(sc);
 
-	if (oc->flags & CF_SHUTW)
+	if (chn_cons(oc)->flags & SC_FL_SHUTW)
 		return;
 
 	/* Write not closed, update FD status and timeout for writes */
 	if (channel_is_empty(oc)) {
 		/* stop writing */
 		if (!sc_ep_test(sc, SE_FL_WAIT_DATA)) {
-			if ((oc->flags & CF_SHUTW_NOW) == 0)
+			if ((chn_cons(oc)->flags & SC_FL_SHUTW_NOW) == 0)
 				sc_ep_set(sc, SE_FL_WAIT_DATA);
 		}
 		return;
@@ -1034,17 +1042,17 @@
 	if (channel_is_empty(oc)) {
 		struct connection *conn = sc_conn(sc);
 
-		if (((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW) &&
+		if (((chn_cons(oc)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) == SC_FL_SHUTW_NOW) &&
 		    (sc->state == SC_ST_EST) && (!conn || !(conn->flags & (CO_FL_WAIT_XPRT | CO_FL_EARLY_SSL_HS))))
 			sc_shutw(sc);
 	}
 
 	/* indicate that we may be waiting for data from the output channel or
-	 * we're about to close and can't expect more data if SHUTW_NOW is there.
+	 * we're about to close and can't expect more data if SC_FL_SHUTW_NOW is there.
 	 */
-	if (!(oc->flags & (CF_SHUTW|CF_SHUTW_NOW)))
+	if (!(chn_cons(oc)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)))
 		sc_ep_set(sc, SE_FL_WAIT_DATA);
-	else if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW)
+	else if ((chn_cons(oc)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) == SC_FL_SHUTW_NOW)
 		sc_ep_clr(sc, SE_FL_WAIT_DATA);
 
 	if (oc->flags & CF_DONT_READ)
@@ -1100,18 +1108,19 @@
 	     *                  data received and no fast-forwarding (CF_READ_EVENT + !to_forward)
 	     *                  read event while consumer side is not established (CF_READ_EVENT + sco->state != SC_ST_EST)
 	     */
-		((ic->flags & CF_READ_EVENT) && ((sc->flags & SC_FL_EOI) || (ic->flags & CF_SHUTR) || !ic->to_forward || sco->state != SC_ST_EST)) ||
+		((ic->flags & CF_READ_EVENT) && ((sc->flags & SC_FL_EOI) || (chn_prod(ic)->flags & SC_FL_SHUTR) || !ic->to_forward || sco->state != SC_ST_EST)) ||
 	    sc_ep_test(sc, SE_FL_ERROR) ||
 
 	    /* changes on the consumption side */
 	    sc_ep_test(sc, SE_FL_ERR_PENDING) ||
 	    ((oc->flags & CF_WRITE_EVENT) &&
 	     ((sc->state < SC_ST_EST) ||
-	      (oc->flags & CF_SHUTW) ||
+	      (chn_cons(oc)->flags & SC_FL_SHUTW) ||
 	      (((oc->flags & CF_WAKE_WRITE) ||
-	       !(oc->flags & (CF_AUTO_CLOSE|CF_SHUTW_NOW|CF_SHUTW))) &&
-	      (sco->state != SC_ST_EST ||
-	       (channel_is_empty(oc) && !oc->to_forward)))))) {
+		(!(oc->flags & CF_AUTO_CLOSE) &&
+		 !(chn_cons(oc)->flags & (SC_FL_SHUTW_NOW|SC_FL_SHUTW)))) &&
+	       (sco->state != SC_ST_EST ||
+		(channel_is_empty(oc) && !oc->to_forward)))))) {
 		task_wakeup(task, TASK_WOKEN_IO);
 	}
 
@@ -1131,15 +1140,16 @@
 
 	BUG_ON(!sc_conn(sc));
 
-	if (ic->flags & CF_SHUTR)
+	if (chn_prod(ic)->flags & SC_FL_SHUTR)
 		return;
-	ic->flags |= CF_SHUTR|CF_READ_EVENT;
+	chn_prod(ic)->flags |= SC_FL_SHUTR;
+	ic->flags |= CF_READ_EVENT;
 	sc_ep_report_read_activity(sc);
 
 	if (!sc_state_in(sc->state, SC_SB_CON|SC_SB_RDY|SC_SB_EST))
 		return;
 
-	if (oc->flags & CF_SHUTW)
+	if (chn_cons(oc)->flags & SC_FL_SHUTW)
 		goto do_close;
 
 	if (sc_cond_forward_shutw(sc)) {
@@ -1156,8 +1166,8 @@
 	/* OK we completely close the socket here just as if we went through sc_shut[rw]() */
 	sc_conn_shut(sc);
 
-	oc->flags &= ~CF_SHUTW_NOW;
-	oc->flags |= CF_SHUTW;
+	chn_cons(oc)->flags &= ~SC_FL_SHUTW_NOW;
+	chn_cons(oc)->flags |= SC_FL_SHUTW;
 
 	sc->state = SC_ST_DIS;
 	if (sc->flags & SC_FL_ISBACK)
@@ -1189,7 +1199,7 @@
 		return 0;
 
 	/* maybe we were called immediately after an asynchronous shutr */
-	if (ic->flags & CF_SHUTR)
+	if (chn_prod(ic)->flags & SC_FL_SHUTR)
 		return 1;
 
 	/* we must wait because the mux is not installed yet */
@@ -1320,7 +1330,7 @@
 	 */
 	while (sc_ep_test(sc, SE_FL_RCV_MORE) ||
 	       (!(conn->flags & CO_FL_HANDSHAKE) &&
-	       (!sc_ep_test(sc, SE_FL_ERROR | SE_FL_EOS)) && !(ic->flags & CF_SHUTR))) {
+		(!sc_ep_test(sc, SE_FL_ERROR | SE_FL_EOS)) && !(chn_prod(ic)->flags & SC_FL_SHUTR))) {
 		int cur_flags = flags;
 
 		/* Compute transient CO_RFL_* flags */
@@ -1361,7 +1371,7 @@
 		cur_read += ret;
 
 		/* if we're allowed to directly forward data, we must update ->o */
-		if (ic->to_forward && !(ic->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
+		if (ic->to_forward && !(chn_cons(ic)->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW))) {
 			unsigned long fwd = ret;
 			if (ic->to_forward != CHN_INFINITE_FORWARD) {
 				if (fwd > ic->to_forward)
@@ -1484,7 +1494,7 @@
 	if (sc_ep_test(sc, SE_FL_ERROR))
 		ret = 1;
 	else if (!(sc->flags & (SC_FL_WONT_READ|SC_FL_NEED_BUFF|SC_FL_NEED_ROOM)) &&
-		 !(ic->flags & CF_SHUTR)) {
+		 !(chn_prod(ic)->flags & SC_FL_SHUTR)) {
 		/* Subscribe to receive events if we're blocking on I/O */
 		conn->mux->subscribe(sc, SUB_RETRY_RECV, &sc->wait_event);
 		se_have_no_more_data(sc->sedesc);
@@ -1554,7 +1564,7 @@
 		return 0;
 
 	/* we might have been called just after an asynchronous shutw */
-	if (oc->flags & CF_SHUTW)
+	if (chn_cons(oc)->flags & SC_FL_SHUTW)
 		return 1;
 
 	/* we must wait because the mux is not installed yet */
@@ -1599,9 +1609,10 @@
 		     ((oc->to_forward && oc->to_forward != CHN_INFINITE_FORWARD) ||
 		      (sc->flags & SC_FL_SND_EXP_MORE) ||
 		      (IS_HTX_STRM(s) &&
-		       (!(sco->flags & SC_FL_EOI) && !(oc->flags & CF_SHUTR) && htx_expect_more(htxbuf(&oc->buf)))))) ||
+		       (!(sco->flags & SC_FL_EOI) && !(chn_prod(oc)->flags & SC_FL_SHUTR) && htx_expect_more(htxbuf(&oc->buf)))))) ||
 		    ((oc->flags & CF_ISRESP) &&
-		     ((oc->flags & (CF_AUTO_CLOSE|CF_SHUTW_NOW)) == (CF_AUTO_CLOSE|CF_SHUTW_NOW))))
+		     (oc->flags & CF_AUTO_CLOSE) &&
+		     (chn_cons(oc)->flags & SC_FL_SHUTW_NOW)))
 			send_flag |= CO_SFL_MSG_MORE;
 
 		if (oc->flags & CF_STREAMER)
@@ -1685,7 +1696,7 @@
 
 	oc->flags &= ~CF_WRITE_EVENT;
 
-	if (oc->flags & CF_SHUTW)
+	if (chn_cons(oc)->flags & SC_FL_SHUTW)
 		return;
 
 	if (channel_is_empty(oc))
@@ -1763,7 +1774,7 @@
 	 *       wake callback. Otherwise sc_conn_recv()/sc_conn_send() already take
 	 *       care of it.
 	 */
-	if (sc_ep_test(sc, SE_FL_EOS) && !(ic->flags & CF_SHUTR)) {
+	if (sc_ep_test(sc, SE_FL_EOS) && !(chn_prod(ic)->flags & SC_FL_SHUTR)) {
 		/* we received a shutdown */
 		if (ic->flags & CF_AUTO_CLOSE)
 			channel_shutw_now(ic);
@@ -1842,7 +1853,7 @@
 	/* If the applet wants to write and the channel is closed, it's a
 	 * broken pipe and it must be reported.
 	 */
-	if (!sc_ep_test(sc, SE_FL_HAVE_NO_DATA) && (ic->flags & CF_SHUTR))
+	if (!sc_ep_test(sc, SE_FL_HAVE_NO_DATA) && (chn_prod(ic)->flags & SC_FL_SHUTR))
 		sc_ep_set(sc, SE_FL_ERROR);
 
 	/* automatically mark the applet having data available if it reported
diff --git a/src/stick_table.c b/src/stick_table.c
index d748705..bb4dd5a 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -5043,8 +5043,8 @@
 	 *   - STATE_DONE : nothing left to dump, the buffer may contain some
 	 *     data though.
 	 */
-
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW)) {
+	/* FIXME: Don't watch the other side !*/
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW)) {
 		/* in case of abort, remove any refcount we might have set on an entry */
 		if (ctx->state == STATE_DUMP) {
 			stksess_kill_if_expired(ctx->t, ctx->entry, 1);
diff --git a/src/stream.c b/src/stream.c
index 5794fd9..b53a603 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -879,7 +879,7 @@
  * SC_ST_EST state. It must only be called after switching from SC_ST_CON (or
  * SC_ST_INI or SC_ST_RDY) to SC_ST_EST, but only when a ->proto is defined.
  * Note that it will switch the interface to SC_ST_DIS if we already have
- * the CF_SHUTR flag, it means we were able to forward the request, and
+ * the SC_FL_SHUTR flag, it means we were able to forward the request, and
  * receive the response, before process_stream() had the opportunity to
  * make the switch from SC_ST_CON to SC_ST_EST. When that happens, we want
  * to go through back_establish() anyway, to make sure the analysers run.
@@ -948,7 +948,7 @@
 	}
 	/* If we managed to get the whole response, and we don't have anything
 	 * left to send, or can't, switch to SC_ST_DIS now. */
-	if (rep->flags & (CF_SHUTR | CF_SHUTW)) {
+	if ((s->scb->flags & SC_FL_SHUTR) || (s->scf->flags & SC_FL_SHUTW)) {
 		s->scb->state = SC_ST_DIS;
 		DBG_TRACE_STATE("response channel shutdwn for read/write", STRM_EV_STRM_PROC|STRM_EV_CS_ST|STRM_EV_STRM_ERR, s);
 	}
@@ -1578,12 +1578,12 @@
 
 	sc_check_timeouts(s->scf);
 	channel_check_timeout(&s->req);
-	if (unlikely((s->req.flags & (CF_SHUTW|CF_WRITE_TIMEOUT)) == CF_WRITE_TIMEOUT)) {
+	if (unlikely(!(s->scb->flags & SC_FL_SHUTW) && (s->req.flags & CF_WRITE_TIMEOUT))) {
 		s->scb->flags |= SC_FL_NOLINGER;
 		sc_shutw(s->scb);
 	}
 
-	if (unlikely((s->req.flags & (CF_SHUTR|CF_READ_TIMEOUT)) == CF_READ_TIMEOUT)) {
+	if (unlikely(!(s->scf->flags & SC_FL_SHUTR) && (s->req.flags & CF_READ_TIMEOUT))) {
 		if (s->scf->flags & SC_FL_NOHALF)
 			s->scf->flags |= SC_FL_NOLINGER;
 		sc_shutr(s->scf);
@@ -1591,16 +1591,16 @@
 
 	sc_check_timeouts(s->scb);
 	channel_check_timeout(&s->res);
-	if (unlikely((s->res.flags & (CF_SHUTW|CF_WRITE_TIMEOUT)) == CF_WRITE_TIMEOUT)) {
+	if (unlikely(!(s->scf->flags & SC_FL_SHUTW) && (s->res.flags & CF_WRITE_TIMEOUT))) {
 		s->scf->flags |= SC_FL_NOLINGER;
 		sc_shutw(s->scf);
 	}
 
-	if (unlikely((s->res.flags & (CF_SHUTR|CF_READ_TIMEOUT)) == CF_READ_TIMEOUT)) {
+	if (unlikely(!(s->scb->flags & SC_FL_SHUTR) && (s->res.flags & CF_READ_TIMEOUT))) {
 		if (s->scb->flags & SC_FL_NOHALF)
 			s->scb->flags |= SC_FL_NOLINGER;
 		sc_shutr(s->scb);
-		}
+	}
 
 	if (HAS_FILTERS(s))
 		flt_stream_check_timeouts(s);
@@ -1711,6 +1711,7 @@
 	struct server *srv;
 	struct stream *s = context;
 	struct session *sess = s->sess;
+	unsigned int scf_flags, scb_flags;
 	unsigned int rqf_last, rpf_last;
 	unsigned int rq_prod_last, rq_cons_last;
 	unsigned int rp_cons_last, rp_prod_last;
@@ -1760,6 +1761,10 @@
 	scf->flags |= SC_FL_DONT_WAKE;
 	scb->flags |= SC_FL_DONT_WAKE;
 
+	/* Keep a copy of SC flags */
+	scf_flags = scf->flags;
+	scb_flags = scb->flags;
+
 	/* update pending events */
 	s->pending_events |= (state & TASK_WOKEN_ANY);
 
@@ -1774,9 +1779,8 @@
 		 * So let's not run a whole stream processing if only an expiration
 		 * timeout needs to be refreshed.
 		 */
-		if (!((req->flags | res->flags) &
-		      (CF_SHUTR|CF_READ_EVENT|CF_READ_TIMEOUT|CF_SHUTW|
-		       CF_WRITE_EVENT|CF_WRITE_TIMEOUT)) &&
+		if (!((scf->flags | scb->flags) & (SC_FL_SHUTR|SC_FL_SHUTW)) &&
+		    !((req->flags | res->flags) & (CF_READ_EVENT|CF_READ_TIMEOUT|CF_WRITE_EVENT|CF_WRITE_TIMEOUT)) &&
 		    !(s->flags & SF_CONN_EXP) &&
 		    !((sc_ep_get(scf) | scb->flags) & SE_FL_ERROR) &&
 		    ((s->pending_events & TASK_WOKEN_ANY) == TASK_WOKEN_TIMER)) {
@@ -1946,12 +1950,14 @@
  resync_request:
 	/* Analyse request */
 	if (((req->flags & ~rqf_last) & CF_MASK_ANALYSER) ||
-	    ((req->flags ^ rqf_last) & CF_MASK_STATIC) ||
-	    (req->analysers && (req->flags & CF_SHUTW)) ||
+	    ((scf->flags ^ scf_flags) & (SC_FL_SHUTR|SC_FL_SHUTR_NOW)) ||
+	    ((scb->flags ^ scb_flags) & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) ||
+	    (req->analysers && (chn_cons(req)->flags & SC_FL_SHUTW)) ||
 	    scf->state != rq_prod_last ||
 	    scb->state != rq_cons_last ||
 	    s->pending_events & TASK_WOKEN_MSG) {
-		unsigned int flags = req->flags;
+		unsigned int scf_flags_ana = scf->flags;
+		unsigned int scb_flags_ana = scb->flags;
 
 		if (sc_state_in(scf->state, SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) {
 			int max_loops = global.tune.maxpollevents;
@@ -2032,8 +2038,10 @@
 		rq_cons_last = scb->state;
 		req->flags &= ~CF_WAKE_ONCE;
 		rqf_last = req->flags;
+		scf_flags = (scf_flags & ~(SC_FL_SHUTR|SC_FL_SHUTR_NOW)) | (scf->flags & (SC_FL_SHUTR|SC_FL_SHUTR_NOW));
+		scb_flags = (scb_flags & ~(SC_FL_SHUTW|SC_FL_SHUTW_NOW)) | (scb->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW));
 
-		if ((req->flags ^ flags) & (CF_SHUTR|CF_SHUTW))
+		if (((scf->flags ^ scf_flags_ana) & SC_FL_SHUTR) || ((scb->flags ^ scb_flags_ana) & SC_FL_SHUTW))
 			goto resync_request;
 	}
 
@@ -2047,12 +2055,14 @@
 	/* Analyse response */
 
 	if (((res->flags & ~rpf_last) & CF_MASK_ANALYSER) ||
-		 (res->flags ^ rpf_last) & CF_MASK_STATIC ||
-		 (res->analysers && (res->flags & CF_SHUTW)) ||
-		 scf->state != rp_cons_last ||
-		 scb->state != rp_prod_last ||
-		 s->pending_events & TASK_WOKEN_MSG) {
-		unsigned int flags = res->flags;
+	    ((scb->flags ^ scb_flags) & (SC_FL_SHUTR|SC_FL_SHUTR_NOW)) ||
+	    ((scf->flags ^ scf_flags) & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) ||
+	    (res->analysers && (chn_cons(res)->flags & SC_FL_SHUTW)) ||
+	    scf->state != rp_cons_last ||
+	    scb->state != rp_prod_last ||
+	    s->pending_events & TASK_WOKEN_MSG) {
+		unsigned int scb_flags_ana = scb->flags;
+		unsigned int scf_flags_ana = scf->flags;
 
 		if (sc_state_in(scb->state, SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) {
 			int max_loops = global.tune.maxpollevents;
@@ -2101,8 +2111,10 @@
 		rp_prod_last = scb->state;
 		res->flags &= ~CF_WAKE_ONCE;
 		rpf_last = res->flags;
+		scb_flags = (scb_flags & ~(SC_FL_SHUTR|SC_FL_SHUTR_NOW)) | (scb->flags & (SC_FL_SHUTR|SC_FL_SHUTR_NOW));
+		scf_flags = (scf_flags & ~(SC_FL_SHUTW|SC_FL_SHUTW_NOW)) | (scf->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW));
 
-		if ((res->flags ^ flags) & (CF_SHUTR|CF_SHUTW))
+		if (((scb->flags ^ scb_flags_ana) & SC_FL_SHUTR) || ((scf->flags ^ scf_flags_ana) & SC_FL_SHUTW))
 			goto resync_response;
 	}
 
@@ -2212,13 +2224,13 @@
 
 	/* If no one is interested in analysing data, it's time to forward
 	 * everything. We configure the buffer to forward indefinitely.
-	 * Note that we're checking CF_SHUTR_NOW as an indication of a possible
+	 * Note that we're checking SC_FL_SHUTR_NOW as an indication of a possible
 	 * recent call to channel_abort().
 	 */
 	if (unlikely((!req->analysers || (req->analysers == AN_REQ_FLT_END && !(req->flags & CF_FLT_ANALYZE))) &&
-	    !(req->flags & (CF_SHUTW|CF_SHUTR_NOW)) &&
-	    (sc_state_in(scf->state, SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) &&
-	    (req->to_forward != CHN_INFINITE_FORWARD))) {
+		     !(scf->flags & SC_FL_SHUTR_NOW) && !(scb->flags & SC_FL_SHUTW) &&
+		     (sc_state_in(scf->state, SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) &&
+		     (req->to_forward != CHN_INFINITE_FORWARD))) {
 		/* This buffer is freewheeling, there's no analyser
 		 * attached to it. If any data are left in, we'll permit them to
 		 * move.
@@ -2234,7 +2246,8 @@
 			 * to the consumer.
 			 */
 			co_set_data(req, htx->data);
-			if ((global.tune.options & GTUNE_USE_FAST_FWD) && !(req->flags & (CF_SHUTR|CF_SHUTW_NOW)))
+			if ((global.tune.options & GTUNE_USE_FAST_FWD) &&
+			    !(scf->flags & SC_FL_SHUTR) && !(scb->flags & SC_FL_SHUTW_NOW))
 				channel_htx_forward_forever(req, htx);
 		}
 		else {
@@ -2242,13 +2255,15 @@
 			 * to the consumer (which might possibly not be connected yet).
 			 */
 			c_adv(req, ci_data(req));
-			if ((global.tune.options & GTUNE_USE_FAST_FWD) && !(req->flags & (CF_SHUTR|CF_SHUTW_NOW)))
+			if ((global.tune.options & GTUNE_USE_FAST_FWD) &&
+			    !(scf->flags & SC_FL_SHUTR) && !(scb->flags & SC_FL_SHUTW_NOW))
 				channel_forward_forever(req);
 		}
 	}
 
 	/* check if it is wise to enable kernel splicing to forward request data */
-	if (!(req->flags & (CF_KERN_SPLICING|CF_SHUTR)) &&
+	if (!(req->flags & CF_KERN_SPLICING) &&
+	    !(scf->flags & SC_FL_SHUTR) &&
 	    req->to_forward &&
 	    (global.tune.options & GTUNE_USE_SPLICE) &&
 	    (sc_conn(scf) && __sc_conn(scf)->xprt && __sc_conn(scf)->xprt->rcv_pipe &&
@@ -2264,6 +2279,8 @@
 
 	/* reflect what the L7 analysers have seen last */
 	rqf_last = req->flags;
+	scf_flags = (scf_flags & ~(SC_FL_SHUTR|SC_FL_SHUTR_NOW)) | (scf->flags & (SC_FL_SHUTR|SC_FL_SHUTR_NOW));
+	scb_flags = (scb_flags & ~(SC_FL_SHUTW|SC_FL_SHUTW_NOW)) | (scb->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW));
 
 	/* it's possible that an upper layer has requested a connection setup or abort.
 	 * There are 2 situations where we decide to establish a new connection :
@@ -2271,7 +2288,7 @@
 	 *  - the CF_AUTO_CONNECT flag is set (active connection)
 	 */
 	if (scb->state == SC_ST_INI) {
-		if (!(req->flags & CF_SHUTW)) {
+		if (!(scb->flags & SC_FL_SHUTW)) {
 			if ((req->flags & CF_AUTO_CONNECT) || !channel_is_empty(req)) {
 				/* If we have an appctx, there is no connect method, so we
 				 * immediately switch to the connected state, otherwise we
@@ -2343,14 +2360,14 @@
 	 * the other side's timeout as well. However this doesn't have effect during the
 	 * connection setup unless the backend has abortonclose set.
 	 */
-	if (unlikely((req->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CLOSE|CF_SHUTR)) ==
-		     (CF_AUTO_CLOSE|CF_SHUTR) &&
+	if (unlikely((req->flags & CF_AUTO_CLOSE) && (scf->flags & SC_FL_SHUTR) &&
+		     !(scb->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) &&
 		     (scb->state != SC_ST_CON || (s->be->options & PR_O_ABRT_CLOSE)))) {
 		channel_shutw_now(req);
 	}
 
 	/* shutdown(write) pending */
-	if (unlikely((req->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW &&
+	if (unlikely((scb->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) == SC_FL_SHUTW_NOW &&
 		     channel_is_empty(req))) {
 		if (sc_ep_test(s->scf, SE_FL_ERROR))
 			scb->flags |= SC_FL_NOLINGER;
@@ -2358,12 +2375,12 @@
 	}
 
 	/* shutdown(write) done on server side, we must stop the client too */
-	if (unlikely((req->flags & (CF_SHUTW|CF_SHUTR|CF_SHUTR_NOW)) == CF_SHUTW &&
-		     !req->analysers))
+	if (unlikely((scb->flags & SC_FL_SHUTW) && !(scf->flags & (SC_FL_SHUTR|SC_FL_SHUTR_NOW))) &&
+	    !req->analysers)
 		channel_shutr_now(req);
 
 	/* shutdown(read) pending */
-	if (unlikely((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) == CF_SHUTR_NOW)) {
+	if (unlikely((scf->flags & (SC_FL_SHUTR|SC_FL_SHUTR_NOW)) == SC_FL_SHUTR_NOW)) {
 		if (scf->flags & SC_FL_NOHALF)
 			scf->flags |= SC_FL_NOLINGER;
 		sc_shutr(scf);
@@ -2377,20 +2394,20 @@
 		goto resync_stconns;
 
 	/* otherwise we want to check if we need to resync the req buffer or not */
-	if ((req->flags ^ rqf_last) & (CF_SHUTR|CF_SHUTW))
+	if (((scf->flags ^ scf_flags) & SC_FL_SHUTR) || ((scb->flags ^ scb_flags) & SC_FL_SHUTW))
 		goto resync_request;
 
 	/* perform output updates to the response buffer */
 
 	/* If no one is interested in analysing data, it's time to forward
 	 * everything. We configure the buffer to forward indefinitely.
-	 * Note that we're checking CF_SHUTR_NOW as an indication of a possible
+	 * Note that we're checking SC_FL_SHUTR_NOW as an indication of a possible
 	 * recent call to channel_abort().
 	 */
 	if (unlikely((!res->analysers || (res->analysers == AN_RES_FLT_END && !(res->flags & CF_FLT_ANALYZE))) &&
-	    !(res->flags & (CF_SHUTW|CF_SHUTR_NOW)) &&
-	    sc_state_in(scb->state, SC_SB_EST|SC_SB_DIS|SC_SB_CLO) &&
-	    (res->to_forward != CHN_INFINITE_FORWARD))) {
+		     !(scf->flags & SC_FL_SHUTR_NOW) && !(scb->flags & SC_FL_SHUTW_NOW) &&
+		     sc_state_in(scb->state, SC_SB_EST|SC_SB_DIS|SC_SB_CLO) &&
+		     (res->to_forward != CHN_INFINITE_FORWARD))) {
 		/* This buffer is freewheeling, there's no analyser
 		 * attached to it. If any data are left in, we'll permit them to
 		 * move.
@@ -2405,7 +2422,8 @@
 			 * to the consumer.
 			 */
 			co_set_data(res, htx->data);
-			if ((global.tune.options & GTUNE_USE_FAST_FWD) && !(res->flags & (CF_SHUTR|CF_SHUTW_NOW)))
+			if ((global.tune.options & GTUNE_USE_FAST_FWD) &&
+			    !(scf->flags & SC_FL_SHUTR) && !(scb->flags & SC_FL_SHUTW_NOW))
 				channel_htx_forward_forever(res, htx);
 		}
 		else {
@@ -2413,7 +2431,8 @@
 			 * to the consumer.
 			 */
 			c_adv(res, ci_data(res));
-			if ((global.tune.options & GTUNE_USE_FAST_FWD) && !(res->flags & (CF_SHUTR|CF_SHUTW_NOW)))
+			if ((global.tune.options & GTUNE_USE_FAST_FWD) &&
+			    !(scf->flags & SC_FL_SHUTR) && !(scb->flags & SC_FL_SHUTW_NOW))
 				channel_forward_forever(res);
 		}
 
@@ -2424,15 +2443,16 @@
 		if (!req->analysers && s->tunnel_timeout) {
 			scf->ioto = scb->ioto = s->tunnel_timeout;
 
-			if (((req->flags & CF_SHUTR) || (res->flags & CF_SHUTW)) && tick_isset(sess->fe->timeout.clientfin))
+			if ((scf->flags & (SC_FL_SHUTR|SC_FL_SHUTW)) && tick_isset(sess->fe->timeout.clientfin))
 				scf->ioto = sess->fe->timeout.clientfin;
-			if (((res->flags & CF_SHUTR) || (req->flags & CF_SHUTW)) && tick_isset(s->be->timeout.serverfin))
+			if ((scb->flags & (SC_FL_SHUTR|SC_FL_SHUTW)) && tick_isset(s->be->timeout.serverfin))
 				scb->ioto = s->be->timeout.serverfin;
 		}
 	}
 
 	/* check if it is wise to enable kernel splicing to forward response data */
-	if (!(res->flags & (CF_KERN_SPLICING|CF_SHUTR)) &&
+	if (!(res->flags & CF_KERN_SPLICING) &&
+	    !(scb->flags & SC_FL_SHUTR) &&
 	    res->to_forward &&
 	    (global.tune.options & GTUNE_USE_SPLICE) &&
 	    (sc_conn(scf) && __sc_conn(scf)->xprt && __sc_conn(scf)->xprt->snd_pipe &&
@@ -2448,6 +2468,8 @@
 
 	/* reflect what the L7 analysers have seen last */
 	rpf_last = res->flags;
+	scb_flags = (scb_flags & ~(SC_FL_SHUTR|SC_FL_SHUTR_NOW)) | (scb->flags & (SC_FL_SHUTR|SC_FL_SHUTR_NOW));
+	scf_flags = (scf_flags & ~(SC_FL_SHUTW|SC_FL_SHUTW_NOW)) | (scf->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW));
 
 	/* Let's see if we can send the pending response now */
 	sc_conn_sync_send(scf);
@@ -2461,24 +2483,24 @@
 	 */
 
 	/* first, let's check if the response buffer needs to shutdown(write) */
-	if (unlikely((res->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CLOSE|CF_SHUTR)) ==
-		     (CF_AUTO_CLOSE|CF_SHUTR))) {
+	if (unlikely((res->flags & CF_AUTO_CLOSE) && (scb->flags & SC_FL_SHUTR) &&
+		     !(scf->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)))) {
 		channel_shutw_now(res);
 	}
 
 	/* shutdown(write) pending */
-	if (unlikely((res->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW &&
+	if (unlikely((scf->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)) == SC_FL_SHUTW_NOW &&
 		     channel_is_empty(res))) {
 		sc_shutw(scf);
 	}
 
 	/* shutdown(write) done on the client side, we must stop the server too */
-	if (unlikely((res->flags & (CF_SHUTW|CF_SHUTR|CF_SHUTR_NOW)) == CF_SHUTW) &&
+	if (unlikely((scf->flags & SC_FL_SHUTW) && !(scb->flags & (SC_FL_SHUTR|SC_FL_SHUTR_NOW))) &&
 	    !res->analysers)
 		channel_shutr_now(res);
 
 	/* shutdown(read) pending */
-	if (unlikely((res->flags & (CF_SHUTR|CF_SHUTR_NOW)) == CF_SHUTR_NOW)) {
+	if (unlikely((scb->flags & (SC_FL_SHUTR|SC_FL_SHUTR_NOW)) == SC_FL_SHUTR_NOW)) {
 		if (scb->flags & SC_FL_NOHALF)
 			scb->flags |= SC_FL_NOLINGER;
 		sc_shutr(scb);
@@ -2493,7 +2515,8 @@
 	if ((req->flags & ~rqf_last) & CF_MASK_ANALYSER)
 		goto resync_request;
 
-	if ((res->flags ^ rpf_last) & CF_MASK_STATIC)
+	if (((scb->flags ^ scb_flags) & (SC_FL_SHUTR|SC_FL_SHUTR_NOW)) ||
+	    ((scf->flags ^ scf_flags) & (SC_FL_SHUTW|SC_FL_SHUTW_NOW)))
 		goto resync_response;
 
 	if (((req->flags ^ rqf_last) | (res->flags ^ rpf_last)) & CF_MASK_ANALYSER)
@@ -2751,7 +2774,7 @@
 /* kill a stream and set the termination flags to <why> (one of SF_ERR_*) */
 void stream_shutdown(struct stream *stream, int why)
 {
-	if (stream->req.flags & (CF_SHUTW|CF_SHUTW_NOW))
+	if (stream->scb->flags & (SC_FL_SHUTW|SC_FL_SHUTW_NOW))
 		return;
 
 	channel_shutw_now(&stream->req);
@@ -3576,7 +3599,7 @@
 		goto done;
 	}
 
-	if (unlikely(sc_ic(sc)->flags & CF_SHUTW)) {
+	if (unlikely(chn_cons(sc_ic(sc))->flags & SC_FL_SHUTW)) {
 		/* If we're forced to shut down, we might have to remove our
 		 * reference to the last stream being dumped.
 		 */
diff --git a/src/tcp_rules.c b/src/tcp_rules.c
index bb4e51f..10a45dd 100644
--- a/src/tcp_rules.c
+++ b/src/tcp_rules.c
@@ -116,7 +116,7 @@
 	 * - if one rule returns KO, then return KO
 	 */
 
-	if ((s->scf->flags & SC_FL_EOI) || (req->flags & CF_SHUTR) || channel_full(req, global.tune.maxrewrite) ||
+	if ((s->scf->flags & (SC_FL_EOI|SC_FL_SHUTR)) || channel_full(req, global.tune.maxrewrite) ||
 	    sc_waiting_room(chn_prod(req)) ||
 	    !s->be->tcp_req.inspect_delay || tick_is_expired(s->rules_exp, now_ms)) {
 		partial = SMP_OPT_FINAL;
@@ -299,7 +299,7 @@
 	 * - if one rule returns OK, then return OK
 	 * - if one rule returns KO, then return KO
 	 */
-	if ((s->scb->flags & SC_FL_EOI) || (rep->flags & CF_SHUTR) || channel_full(rep, global.tune.maxrewrite) ||
+	if ((s->scb->flags & (SC_FL_EOI|SC_FL_SHUTR)) || channel_full(rep, global.tune.maxrewrite) ||
 	    sc_waiting_room(chn_prod(rep)) ||
 	    !s->be->tcp_rep.inspect_delay || tick_is_expired(s->rules_exp, now_ms)) {
 		partial = SMP_OPT_FINAL;