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.
This commit is contained in:
Willy Tarreau 2013-12-17 00:00:28 +01:00
parent 4bfa4228dc
commit 2737562e43
3 changed files with 68 additions and 0 deletions

View 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.

View 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;

View 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