MEDIUM: stream-int: implement a very simplistic idle connection manager

Idle connections are not monitored right now. So if a server closes after
a response without advertising it, it won't be detected until a next
request wants to use the connection. This is a bit problematic because
it unnecessarily maintains file descriptors and sockets in an idle
state.

This patch implements a very simple idle connection manager for the stream
interface. It presents itself as an I/O callback. The HTTP engine enables
it when it recycles a connection. If a close or an error is detected on the
underlying socket, it tries to drain as much data as possible from the socket,
detect the close and responds with a close as well, then detaches from the
stream interface.
diff --git a/include/proto/stream_interface.h b/include/proto/stream_interface.h
index 938ccc1..6d4da78 100644
--- a/include/proto/stream_interface.h
+++ b/include/proto/stream_interface.h
@@ -41,6 +41,7 @@
 extern struct si_ops si_embedded_ops;
 extern struct si_ops si_conn_ops;
 extern struct data_cb si_conn_cb;
+extern struct data_cb si_idle_conn_cb;
 
 struct appctx *stream_int_register_handler(struct stream_interface *si, struct si_applet *app);
 void stream_int_unregister_handler(struct stream_interface *si);
@@ -138,6 +139,21 @@
 	si->ops = &si_embedded_ops;
 }
 
+/* Turn a possibly existing connection endpoint of stream interface <si> to
+ * idle mode, which means that the connection will be polled for incoming events
+ * and might be killed by the underlying I/O handler.
+ */
+static inline void si_idle_conn(struct stream_interface *si)
+{
+	struct connection *conn = objt_conn(si->end);
+
+	if (!conn)
+		return;
+
+	conn_attach(conn, si, &si_idle_conn_cb);
+	conn_data_want_recv(conn);
+}
+
 /* Attach connection <conn> to the stream interface <si>. The stream interface
  * is configured to work with a connection and the connection it configured
  * with a stream interface data layer.
diff --git a/src/proto_http.c b/src/proto_http.c
index 82863ad..2c6e272 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -4423,6 +4423,9 @@
 	channel_auto_read(s->rep);
 	channel_auto_close(s->rep);
 
+	/* we're in keep-alive with an idle connection, monitor it */
+	si_idle_conn(s->req->cons);
+
 	s->req->analysers = s->listener->analysers;
 	s->rep->analysers = 0;
 
diff --git a/src/stream_interface.c b/src/stream_interface.c
index d819ede..5c4633b 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -49,6 +49,8 @@
 static void si_conn_recv_cb(struct connection *conn);
 static void si_conn_send_cb(struct connection *conn);
 static int si_conn_wake_cb(struct connection *conn);
+static int si_idle_conn_wake_cb(struct connection *conn);
+static void si_idle_conn_null_cb(struct connection *conn);
 
 /* stream-interface operations for embedded tasks */
 struct si_ops si_embedded_ops = {
@@ -74,6 +76,12 @@
 	.wake    = si_conn_wake_cb,
 };
 
+struct data_cb si_idle_conn_cb = {
+	.recv    = si_idle_conn_null_cb,
+	.send    = si_idle_conn_null_cb,
+	.wake    = si_idle_conn_wake_cb,
+};
+
 /*
  * This function only has to be called once after a wakeup event in case of
  * suspected timeout. It controls the stream interface timeouts and sets
@@ -483,6 +491,47 @@
 	return 0;
 }
 
+
+/* Tiny I/O callback called on recv/send I/O events on idle connections.
+ * It simply sets the CO_FL_SOCK_RD_SH flag so that si_idle_conn_wake_cb()
+ * is notified and can kill the connection.
+ */
+static void si_idle_conn_null_cb(struct connection *conn)
+{
+	if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH))
+		return;
+
+	if ((fdtab[conn->t.sock.fd].ev & (FD_POLL_ERR|FD_POLL_HUP)) ||
+	    (conn->ctrl->drain && conn->ctrl->drain(conn->t.sock.fd)))
+		conn->flags |= CO_FL_SOCK_RD_SH;
+
+	/* disable draining if we were called and have no drain function */
+	if (!conn->ctrl->drain)
+		__conn_data_stop_recv(conn);
+}
+
+/* Callback to be used by connection I/O handlers when some activity is detected
+ * on an idle server connection. Its main purpose is to kill the connection once
+ * a close was detected on it. It returns 0 if it did nothing serious, or -1 if
+ * it killed the connection.
+ */
+static int si_idle_conn_wake_cb(struct connection *conn)
+{
+	struct stream_interface *si = conn->owner;
+
+	if (!conn_ctrl_ready(conn))
+		return 0;
+
+	if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH)) {
+		/* warning, we can't do anything on <conn> after this call ! */
+		conn_force_close(conn);
+		conn_free(conn);
+		si->end = NULL;
+		return -1;
+	}
+	return 0;
+}
+
 /* Callback to be used by connection I/O handlers upon completion. It differs from
  * the update function in that it is designed to be called by lower layers after I/O
  * events have been completed. It will also try to wake the associated task up if