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