MEDIUM: conn-stream: Be prepared to use an appctx as conn-stream endpoint

To be able to use an appctx as conn-stream endpoint, the connection is no
longer stored as is in the conn-stream. The obj-type is used instead.
diff --git a/include/haproxy/conn_stream-t.h b/include/haproxy/conn_stream-t.h
index bf07260..fcf893f 100644
--- a/include/haproxy/conn_stream-t.h
+++ b/include/haproxy/conn_stream-t.h
@@ -25,8 +25,6 @@
 
 #include <haproxy/obj_type-t.h>
 
-struct connection;
-
 /* conn_stream flags */
 enum {
 	CS_FL_NONE          = 0x00000000,  /* Just for initialization purposes */
@@ -92,7 +90,7 @@
 	enum obj_type obj_type;              /* differentiates connection from applet context */
 	/* 3 bytes hole here */
 	unsigned int flags;                  /* CS_FL_* */
-	struct connection *conn;             /* xprt-level connection */
+	enum obj_type *end;                  /* points to the end point (connection or appctx) */
 	void *data;                          /* pointer to upper layer's entity (eg: stream interface) */
 	const struct data_cb *data_cb;       /* data layer callbacks. Must be set before xprt->init() */
 	void *ctx;                           /* mux-specific context */
diff --git a/include/haproxy/conn_stream.h b/include/haproxy/conn_stream.h
index bceb7a6..dd52811 100644
--- a/include/haproxy/conn_stream.h
+++ b/include/haproxy/conn_stream.h
@@ -23,6 +23,7 @@
 #define _HAPROXY_CONN_STREAM_H
 
 #include <haproxy/api.h>
+#include <haproxy/applet.h>
 #include <haproxy/connection.h>
 #include <haproxy/conn_stream-t.h>
 #include <haproxy/obj_type.h>
@@ -32,24 +33,29 @@
 
 #define IS_HTX_CS(cs)     (cs_conn(cs) && IS_HTX_CONN(cs_conn(cs)))
 
-struct conn_stream *cs_new(struct connection *conn, void *target);
+struct conn_stream *cs_new(enum obj_type *endp);
 void cs_free(struct conn_stream *cs);
 
 /*
  * Initializes all required fields for a new conn_strema.
  */
-static inline void cs_init(struct conn_stream *cs, struct connection *conn)
+static inline void cs_init(struct conn_stream *cs, enum obj_type *endp)
 {
 	cs->obj_type = OBJ_TYPE_CS;
 	cs->flags = CS_FL_NONE;
-	cs->conn = conn;
-	cs->ctx = conn;
+	cs->end = endp;
+	if (objt_conn(endp))
+		cs->ctx = endp;
+	cs->data = NULL;
+	cs->data_cb = NULL;
 }
 
-/* Returns the conn from a cs. If cs is NULL, returns NULL */
+/* Returns the connection from a cs if the endpoint is a connection. Otherwise
+ * NULL is returned.
+ */
 static inline struct connection *cs_conn(const struct conn_stream *cs)
 {
-	return cs ? cs->conn : NULL;
+	return (cs ? objt_conn(cs->end) : NULL);
 }
 
 /* Returns the mux of the connection from a cs if the endpoint is a
@@ -62,6 +68,14 @@
 	return (conn ? conn->mux : NULL);
 }
 
+/* Returns the appctx from a cs if the endpoint is an appctx. Otherwise NULL is
+ * returned.
+ */
+static inline struct appctx *cs_appctx(const struct conn_stream *cs)
+{
+	return (cs ? objt_appctx(cs->end) : NULL);
+}
+
 /* Attaches a conn_stream to a data layer and sets the relevant callbacks */
 static inline void cs_attach(struct conn_stream *cs, void *data, const struct data_cb *data_cb)
 {
@@ -77,6 +91,7 @@
 static inline void cs_detach(struct conn_stream *cs)
 {
 	struct connection *conn;
+	struct appctx *appctx;
 
 	if ((conn = cs_conn(cs))) {
 		if (conn->mux)
@@ -92,6 +107,11 @@
 			conn_free(conn);
 		}
 	}
+	else if ((appctx = cs_appctx(cs))) {
+		if (appctx->applet->release)
+			appctx->applet->release(appctx);
+		appctx_free(appctx);
+	}
 	cs_init(cs, NULL);
 }
 
diff --git a/include/haproxy/stream_interface.h b/include/haproxy/stream_interface.h
index 15acc39..8228bad 100644
--- a/include/haproxy/stream_interface.h
+++ b/include/haproxy/stream_interface.h
@@ -240,9 +240,9 @@
 	si_reset_endpoint(si);
 	cs = objt_cs(si->end);
 	if (!cs)
-		cs = cs_new(conn, conn->target);
+		cs = cs_new(&conn->obj_type);
 	if (cs) {
-		cs_init(cs, conn);
+		cs_init(cs, &conn->obj_type);
 		if (!conn->ctx)
 			conn->ctx = cs;
 		si_attach_cs(si, cs);
@@ -431,7 +431,7 @@
 
 	si_release_endpoint(si);
 
-	cs = cs_new(conn, (conn ? conn->target : NULL));
+	cs = cs_new(&conn->obj_type);
 	if (cs)
 		si_attach_cs(si, cs);
 
diff --git a/src/conn_stream.c b/src/conn_stream.c
index 43e6fd0..7b53ecc 100644
--- a/src/conn_stream.c
+++ b/src/conn_stream.c
@@ -19,30 +19,17 @@
 DECLARE_POOL(pool_head_connstream, "conn_stream", sizeof(struct conn_stream));
 
 
-/* Tries to allocate a new conn_stream and initialize its main fields. If
- * <conn> is NULL, then a new connection is allocated on the fly, initialized,
- * and assigned to cs->conn ; this connection will then have to be released
- * using pool_free() or conn_free(). The conn_stream is initialized and added
- * to the mux's stream list on success, then returned. On failure, nothing is
- * allocated and NULL is returned.
+/* Tries to allocate a new conn_stream and initialize its main fields. On
+ * failure, nothing is allocated and NULL is returned.
  */
-struct conn_stream *cs_new(struct connection *conn, void *target)
+struct conn_stream *cs_new(enum obj_type *endp)
 {
 	struct conn_stream *cs;
 
 	cs = pool_alloc(pool_head_connstream);
 	if (unlikely(!cs))
 		return NULL;
-
-	if (!conn) {
-		conn = conn_new(target);
-		if (unlikely(!conn)) {
-			cs_free(cs);
-			return NULL;
-		}
-	}
-
-	cs_init(cs, conn);
+	cs_init(cs, endp);
 	return cs;
 }
 
diff --git a/src/h3.c b/src/h3.c
index 31b9c00..bda2a46 100644
--- a/src/h3.c
+++ b/src/h3.c
@@ -176,7 +176,7 @@
 	if (fin)
 		htx->flags |= HTX_FL_EOM;
 
-	cs = cs_new(qcs->qcc->conn, qcs->qcc->conn->target);
+	cs = cs_new(qcs->qcc->conn->obj_type);
 	if (!cs)
 		return 1;
 
diff --git a/src/hq_interop.c b/src/hq_interop.c
index 0e2c6cd..376779d 100644
--- a/src/hq_interop.c
+++ b/src/hq_interop.c
@@ -72,7 +72,7 @@
 	htx_add_endof(htx, HTX_BLK_EOH);
 	htx_to_buf(htx, &htx_buf);
 
-	cs = cs_new(qcs->qcc->conn, qcs->qcc->conn->target);
+	cs = cs_new(&qcs->qcc->conn->obj_type);
 	if (!cs)
 		return -1;
 
diff --git a/src/mux_h1.c b/src/mux_h1.c
index 81b5cce..cc53364 100644
--- a/src/mux_h1.c
+++ b/src/mux_h1.c
@@ -678,12 +678,13 @@
  * success or NULL on error. */
 static struct conn_stream *h1s_new_cs(struct h1s *h1s, struct buffer *input)
 {
+	struct h1c *h1c = h1s->h1c;
 	struct conn_stream *cs;
 
-	TRACE_ENTER(H1_EV_STRM_NEW, h1s->h1c->conn, h1s);
-	cs = cs_new(h1s->h1c->conn, h1s->h1c->conn->target);
+	TRACE_ENTER(H1_EV_STRM_NEW, h1c->conn, h1s);
+	cs = cs_new(&h1c->conn->obj_type);
 	if (!cs) {
-		TRACE_ERROR("CS allocation failure", H1_EV_STRM_NEW|H1_EV_STRM_END|H1_EV_STRM_ERR, h1s->h1c->conn, h1s);
+		TRACE_ERROR("CS allocation failure", H1_EV_STRM_NEW|H1_EV_STRM_END|H1_EV_STRM_ERR, h1c->conn, h1s);
 		goto err;
 	}
 	h1s->cs = cs;
@@ -696,21 +697,21 @@
 		cs->flags |= CS_FL_WEBSOCKET;
 
 	if (stream_create_from_cs(cs, input) < 0) {
-		TRACE_DEVEL("leaving on stream creation failure", H1_EV_STRM_NEW|H1_EV_STRM_END|H1_EV_STRM_ERR, h1s->h1c->conn, h1s);
+		TRACE_DEVEL("leaving on stream creation failure", H1_EV_STRM_NEW|H1_EV_STRM_END|H1_EV_STRM_ERR, h1c->conn, h1s);
 		goto err;
 	}
 
-	HA_ATOMIC_INC(&h1s->h1c->px_counters->open_streams);
-	HA_ATOMIC_INC(&h1s->h1c->px_counters->total_streams);
+	HA_ATOMIC_INC(&h1c->px_counters->open_streams);
+	HA_ATOMIC_INC(&h1c->px_counters->total_streams);
 
-	h1s->h1c->flags = (h1s->h1c->flags & ~H1C_F_ST_EMBRYONIC) | H1C_F_ST_ATTACHED | H1C_F_ST_READY;
-	TRACE_LEAVE(H1_EV_STRM_NEW, h1s->h1c->conn, h1s);
+	h1c->flags = (h1c->flags & ~H1C_F_ST_EMBRYONIC) | H1C_F_ST_ATTACHED | H1C_F_ST_READY;
+	TRACE_LEAVE(H1_EV_STRM_NEW, h1c->conn, h1s);
 	return cs;
 
   err:
 	cs_free(cs);
 	h1s->cs = NULL;
-	TRACE_DEVEL("leaving on error", H1_EV_STRM_NEW|H1_EV_STRM_ERR, h1s->h1c->conn, h1s);
+	TRACE_DEVEL("leaving on error", H1_EV_STRM_NEW|H1_EV_STRM_ERR, h1c->conn, h1s);
 	return NULL;
 }
 
diff --git a/src/mux_h2.c b/src/mux_h2.c
index c466332..d142793 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -1529,7 +1529,7 @@
 	if (!h2s)
 		goto out;
 
-	cs = cs_new(h2c->conn, h2c->conn->target);
+	cs = cs_new(&h2c->conn->obj_type);
 	if (!cs)
 		goto out_close;
 
diff --git a/src/mux_pt.c b/src/mux_pt.c
index 1f8b4dc..e048a49 100644
--- a/src/mux_pt.c
+++ b/src/mux_pt.c
@@ -291,7 +291,7 @@
 	ctx->conn = conn;
 
 	if (!cs) {
-		cs = cs_new(conn, conn->target);
+		cs = cs_new(&conn->obj_type);
 		if (!cs) {
 			TRACE_ERROR("CS allocation failure", PT_EV_STRM_NEW|PT_EV_STRM_END|PT_EV_STRM_ERR, conn);
 			goto fail_free_ctx;
diff --git a/src/tcpcheck.c b/src/tcpcheck.c
index bf7e5c9..5fec53e 100644
--- a/src/tcpcheck.c
+++ b/src/tcpcheck.c
@@ -1091,8 +1091,10 @@
 	check_release_buf(check, &check->bo);
 
 	/* No connection, prepare a new one */
-	cs = cs_new(NULL, (s ? &s->obj_type : &proxy->obj_type));
-	if (!cs) {
+	conn = conn_new((s ? &s->obj_type : &proxy->obj_type));
+	if (conn)
+		cs = cs_new(&conn->obj_type);
+	if (!conn || !cs) {
 		chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
 			     tcpcheck_get_step_id(check, rule));
 		if (rule->comment)
@@ -1100,13 +1102,14 @@
 		set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.area);
 		ret = TCPCHK_EVAL_STOP;
 		TRACE_ERROR("conn-stream allocation error", CHK_EV_TCPCHK_CONN|CHK_EV_TCPCHK_ERR, check);
+		if (conn)
+			conn_free(conn);
 		goto out;
 	}
 
 	tasklet_set_tid(check->wait_list.tasklet, tid);
 
 	check->cs = cs;
-	conn = cs->conn;
 	conn_set_owner(conn, check->sess, NULL);
 
 	/* Maybe there were an older connection we were waiting on */