]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: stream-int: implement a very simplistic idle connection manager
authorWilly Tarreau <w@1wt.eu>
Mon, 16 Dec 2013 23:00:28 +0000 (00:00 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 16 Dec 2013 23:00:28 +0000 (00:00 +0100)
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.

include/proto/stream_interface.h
src/proto_http.c
src/stream_interface.c

index 938ccc1431146e1f24dcaef70dd421032f4530fe..6d4da780680082b31e2e7a18cf2e01b4f189b89b 100644 (file)
@@ -41,6 +41,7 @@ void stream_sock_read0(struct stream_interface *si);
 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 @@ static inline void si_detach(struct stream_interface *si)
        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.
index 82863ad9de591ec244ad0c9a488c4acf723128be..2c6e27273c18c9bf37cfffec0615b0434f350f22 100644 (file)
@@ -4423,6 +4423,9 @@ void http_end_txn_clean_session(struct session *s)
        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;
 
index d819ede827479305f6b2e6b209fc5d3ab8378eae..5c4633b812aab05268f4808ed45586183058b648 100644 (file)
@@ -49,6 +49,8 @@ static void stream_int_chk_snd_conn(struct stream_interface *si);
 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 @@ struct data_cb si_conn_cb = {
        .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 @@ int conn_si_send_proxy(struct connection *conn, unsigned int flag)
        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