diff --git a/include/proto/stream_interface.h b/include/proto/stream_interface.h index 938ccc143..6d4da7806 100644 --- a/include/proto/stream_interface.h +++ b/include/proto/stream_interface.h @@ -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 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 to the stream interface . 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 82863ad9d..2c6e27273 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -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; diff --git a/src/stream_interface.c b/src/stream_interface.c index d819ede82..5c4633b81 100644 --- a/src/stream_interface.c +++ b/src/stream_interface.c @@ -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 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