MEDIUM: sessions: Don't keep an infinite number of idling connections.

In session, don't keep an infinite number of connection that can idle.
Add a new frontend parameter, "max-session-srv-conns" to set a max number,
with a default value of 5.
diff --git a/src/backend.c b/src/backend.c
index c79302d..210cc31 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1254,8 +1254,9 @@
 				    (srv_conn->mux->avail_streams(srv_conn) == 1)) {
 					LIST_DEL(&old_conn->session_list);
 					LIST_INIT(&old_conn->session_list);
-					session_add_conn(sess, old_conn, s->target);
 					old_conn->owner = sess;
+					session_add_conn(sess, old_conn, s->target);
+					session_check_idle_conn(sess, old_conn);
 				}
 			}
 
@@ -1283,6 +1284,7 @@
 	if (srv_conn && old_conn != srv_conn) {
 		srv_conn->owner = s->sess;
 		LIST_DEL(&srv_conn->session_list);
+		LIST_INIT(&srv_conn->session_list);
 		session_add_conn(s->sess, srv_conn, s->target);
 	}
 
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index aa082be..aa2d860 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -421,6 +421,7 @@
 			curproxy->fe_sps_lim = defproxy.fe_sps_lim;
 
 			curproxy->to_log = defproxy.to_log & ~LW_COOKIE & ~LW_REQHDR & ~ LW_RSPHDR;
+			curproxy->max_out_conns = defproxy.max_out_conns;
 		}
 
 		if (curproxy->cap & PR_CAP_BE) {
@@ -1327,6 +1328,17 @@
 		else
 			curproxy->server_state_file_name = strdup(args[1]);
 	}
+	else if (!strcmp(args[0], "max-session-srv-conns")) {
+		if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL))
+			err_code |= ERR_WARN;
+		if (*(args[1]) == 0) {
+			ha_alert("parsine [%s:%d] : '%s' expects a number. Got no argument\n",
+			    file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		curproxy->max_out_conns = atoi(args[1]);
+	}
 	else if (!strcmp(args[0], "capture")) {
 		if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL))
 			err_code |= ERR_WARN;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 977d4bd..7c316df 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -455,6 +455,7 @@
 	defproxy.redispatch_after = 0;
 	defproxy.lbprm.chash.balance_factor = 0;
 	defproxy.options = PR_O_REUSE_SAFE;
+	defproxy.max_out_conns = MAX_SRV_LIST;
 
 	defproxy.defsrv.check.inter = DEF_CHKINTR;
 	defproxy.defsrv.check.fastinter = 0;
diff --git a/src/mux_h1.c b/src/mux_h1.c
index d116cbd..45d24c1 100644
--- a/src/mux_h1.c
+++ b/src/mux_h1.c
@@ -1947,6 +1947,19 @@
 			h1c->conn->owner = sess;
 			session_add_conn(sess, h1c->conn, h1c->conn->target);
 		}
+		if (h1c->conn->owner == sess) {
+			int ret = session_check_idle_conn(sess, h1c->conn);
+			if (ret == -1)
+				/* The connection got destroyed, let's leave */
+				return;
+			else if (ret == 1) {
+				/* The connection was added to the server list,
+				 * wake the task so we can subscribe to events
+				 */
+				tasklet_wakeup(h1c->wait_event.task);
+				return;
+			}
+		}
 		/* we're in keep-alive with an idle connection, monitor it if not already done */
 		if (LIST_ISEMPTY(&h1c->conn->list)) {
 			struct server *srv = objt_server(h1c->conn->target);
diff --git a/src/mux_h2.c b/src/mux_h2.c
index 3509db5..1cd76fd 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -2850,6 +2850,11 @@
 				h2c->conn->owner = sess;
 				session_add_conn(sess, h2c->conn, h2c->conn->target);
 			}
+			if (eb_is_empty(&h2c->streams_by_id)) {
+				if (session_check_idle_conn(h2c->conn->owner, h2c->conn) != 0)
+					/* At this point either the connection is destroyed, or it's been added to the server idle list, just stop */
+					return;
+			}
 			/* Never ever allow to reuse a connection from a non-reuse backend */
 			if ((h2c->proxy->options & PR_O_REUSE_MASK) == PR_O_REUSE_NEVR)
 				h2c->conn->flags |= CO_FL_PRIVATE;
diff --git a/src/proto_http.c b/src/proto_http.c
index 2b9c0f5..100a7ba 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3489,9 +3489,19 @@
 		srv_conn = NULL;
 	else if (!srv_conn->owner) {
 		srv_conn->owner = s->sess;
+		/* Add it unconditionally to the session list, it'll be removed
+		 * later if needed by session_check_idle_conn(), once we'll
+		 * have released the endpoint and know if it no longer has
+		 * attached streams, and so an idling connection
+		 */
 		session_add_conn(s->sess, srv_conn, s->target);
 	}
 	si_release_endpoint(&s->si[1]);
+	if (srv_conn && srv_conn->owner == s->sess) {
+		if (session_check_idle_conn(s->sess, srv_conn) != 0)
+			srv_conn = NULL;
+	}
+
 
 	s->si[1].state     = s->si[1].prev_state = SI_ST_INI;
 	s->si[1].err_type  = SI_ET_NONE;
diff --git a/src/session.c b/src/session.c
index 502f00e..211181c 100644
--- a/src/session.c
+++ b/src/session.c
@@ -64,6 +64,7 @@
 			sess->srv_list[i].target = NULL;
 			LIST_INIT(&sess->srv_list[i].list);
 		}
+		sess->resp_conns = 0;
 	}
 	return sess;
 }
@@ -86,30 +87,11 @@
 		list_for_each_entry_safe(conn, conn_back, &sess->srv_list[i].list, session_list) {
 			count++;
 			if (conn->mux) {
-				struct server *srv;
 
 				LIST_DEL(&conn->session_list);
 				LIST_INIT(&conn->session_list);
-				srv = objt_server(conn->target);
 				conn->owner = NULL;
-				if (srv && srv->pool_purge_delay > 0 &&
-				    (srv->max_idle_conns == -1 ||
-				     srv->max_idle_conns > srv->curr_idle_conns) &&
-				    !(conn->flags & CO_FL_PRIVATE) &&
-				    conn->mux->avail_streams(conn) ==
-				    conn->mux->max_streams(conn)) {
-					LIST_DEL(&conn->list);
-
-					LIST_ADDQ(&srv->idle_orphan_conns[tid],
-					    &conn->list);
-					srv->curr_idle_conns++;
-
-					conn->idle_time = now_ms;
-					if (!(task_in_wq(srv->idle_task[tid])) &&
-					    !(task_in_rq(srv->idle_task[tid])))
-						task_schedule(srv->idle_task[tid],
-						    tick_add(now_ms, srv->pool_purge_delay));
-				} else
+				if (!srv_add_to_idle_list(objt_server(conn->target), conn))
 					conn->mux->destroy(conn);
 			} else {
 				/* We have a connection, but not yet an associated mux.