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/include/proto/connection.h b/include/proto/connection.h
index 7c8c164..6be311c 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -664,7 +664,11 @@
 static inline void conn_free(struct connection *conn)
 {
 	/* Remove ourself from the session's connections list, if any. */
-	LIST_DEL(&conn->session_list);
+	if (!LIST_ISEMPTY(&conn->session_list)) {
+		struct session *sess = conn->owner;
+		sess->resp_conns--;
+		LIST_DEL(&conn->session_list);
+	}
 	/* If we temporarily stored the connection as the stream_interface's
 	 * end point, remove it.
 	 */
diff --git a/include/proto/server.h b/include/proto/server.h
index 75cba47..b3a9b87 100644
--- a/include/proto/server.h
+++ b/include/proto/server.h
@@ -233,6 +233,27 @@
 	return ret;
 }
 
+static inline int srv_add_to_idle_list(struct server *srv, struct connection *conn)
+{
+	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) &&
+	    ((srv->proxy->options & PR_O_REUSE_MASK) != PR_O_REUSE_NEVR) &&
+	    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));
+		return 1;
+	}
+	return 0;
+}
+
 #endif /* _PROTO_SERVER_H */
 
 /*
diff --git a/include/proto/session.h b/include/proto/session.h
index bc5498a..8c99e7d 100644
--- a/include/proto/session.h
+++ b/include/proto/session.h
@@ -30,7 +30,9 @@
 #include <types/global.h>
 #include <types/session.h>
 
+#include <proto/obj_type.h>
 #include <proto/stick_table.h>
+#include <proto/server.h>
 
 extern struct pool_head *pool_head_session;
 struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type *origin);
@@ -76,6 +78,7 @@
 	int avail = -1;
 	int i;
 
+	sess->resp_conns++;
 	for (i = 0; i < MAX_SRV_LIST; i++) {
 		if (sess->srv_list[i].target == target) {
 			avail = i;
@@ -99,6 +102,7 @@
 		}
 		/* Now unown all the connections */
 		list_for_each_entry_safe(conn, conn_back, &sess->srv_list[avail].list, session_list) {
+			sess->resp_conns--;
 			conn->owner = NULL;
 			LIST_DEL(&conn->session_list);
 			LIST_INIT(&conn->session_list);
@@ -111,6 +115,24 @@
 	LIST_ADDQ(&sess->srv_list[avail].list, &conn->session_list);
 }
 
+/* Returns 0 if the session can keep the idle conn, -1 if it was destroyed, or 1 if it was added to the server list */
+static inline int session_check_idle_conn(struct session *sess, struct connection *conn)
+{
+	if (sess->resp_conns > sess->fe->max_out_conns) {
+		/* We can't keep the connection, let's try to add it to the server idle list */
+		LIST_DEL(&conn->session_list);
+		LIST_INIT(&conn->session_list);
+		conn->owner = NULL;
+		sess->resp_conns--;
+		if (!srv_add_to_idle_list(objt_server(conn->target), conn)) {
+			/* The server doesn't want it, let's kill the connection right away */
+			conn->mux->destroy(conn);
+			return -1;
+		}
+		return 1;
+	}
+	return 0;
+}
 
 #endif /* _PROTO_SESSION_H */
 
diff --git a/include/types/proxy.h b/include/types/proxy.h
index b0877a2..14b6046 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -278,6 +278,7 @@
 
 	int options;				/* PR_O_REDISP, PR_O_TRANSP, ... */
 	int options2;				/* PR_O2_* */
+	int max_out_conns;                      /* Max number of idling connections we keep for a session */
 	struct in_addr mon_net, mon_mask;	/* don't forward connections from this net (network order) FIXME: should support IPv6 */
 	unsigned int ck_opts;			/* PR_CK_* (cookie options) */
 	unsigned int fe_req_ana, be_req_ana;	/* bitmap of common request protocol analysers for the frontend and backend */
diff --git a/include/types/session.h b/include/types/session.h
index 334a071..33ce4fc 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -54,6 +54,7 @@
 	struct vars vars;               /* list of variables for the session scope. */
 	struct task *task;              /* handshake timeout processing */
 	long t_handshake;               /* handshake duration, -1 = not completed */
+	int resp_conns;                 /* Number of connections we're currently responsible for */
 	struct sess_srv_list srv_list[MAX_SRV_LIST]; /* List of servers and the connections the session is currently responsible for */
 };