]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: stream-int: split the shutr/shutw functions between applet and conn
authorWilly Tarreau <w@1wt.eu>
Sun, 29 Sep 2013 12:51:58 +0000 (14:51 +0200)
committerWilly Tarreau <w@1wt.eu>
Mon, 9 Dec 2013 14:40:22 +0000 (15:40 +0100)
These functions induce a lot of ifs everywhere because they consider two
different cases, one which is where the connection exists and has a file
descriptor, and the other one which is the default case where at most an
applet has to be notified.

Let's have them in si_ops and automatically decide which one to use.

The connection shutdown sequence has been slightly simplified, and we
now clear the flags at the end.

Also we remove SHUTR_NOW after a shutw with nolinger, as it's cleaner
not to keep it.

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

index c43ef57a192748b31a412cfb3040cc6f36b9779f..f01048c5852f306a49e5aecdd9a256d52adf4e7a 100644 (file)
@@ -36,8 +36,6 @@ int stream_int_check_timeouts(struct stream_interface *si);
 void stream_int_report_error(struct stream_interface *si);
 void stream_int_retnclose(struct stream_interface *si, const struct chunk *msg);
 int conn_si_send_proxy(struct connection *conn, unsigned int flag);
-int stream_int_shutr(struct stream_interface *si);
-int stream_int_shutw(struct stream_interface *si);
 void stream_sock_read0(struct stream_interface *si);
 
 extern struct si_ops si_embedded_ops;
@@ -74,14 +72,14 @@ static inline void si_prepare_embedded(struct stream_interface *si)
 /* Sends a shutr to the connection using the data layer */
 static inline void si_shutr(struct stream_interface *si)
 {
-       if (stream_int_shutr(si))
+       if (si->ops->shutr(si))
                conn_data_stop_recv(si->conn);
 }
 
 /* Sends a shutw to the connection using the data layer */
 static inline void si_shutw(struct stream_interface *si)
 {
-       if (stream_int_shutw(si))
+       if (si->ops->shutw(si))
                conn_data_stop_send(si->conn);
 }
 
index 6748fbfe5eb6e96e97116f85c42abf8693f8eff2..257a5d691296472ebbcc9dd799cf43ed68cb0bd8 100644 (file)
@@ -85,6 +85,8 @@ struct si_ops {
        void (*update)(struct stream_interface *);  /* I/O update function */
        void (*chk_rcv)(struct stream_interface *); /* chk_rcv function */
        void (*chk_snd)(struct stream_interface *); /* chk_snd function */
+       int (*shutr)(struct stream_interface *);    /* shut read function */
+       int (*shutw)(struct stream_interface *);    /* shut write function */
 };
 
 /* A stream interface has 3 parts :
index 4600994412b87faeaea5f93b5a7f6a09c897d492..566a980a7d9b093fbd9aefcf2487dae5741da824 100644 (file)
 
 /* socket functions used when running a stream interface as a task */
 static void stream_int_update_embedded(struct stream_interface *si);
+static int stream_int_shutr(struct stream_interface *si);
+static int stream_int_shutw(struct stream_interface *si);
 static void stream_int_chk_rcv(struct stream_interface *si);
 static void stream_int_chk_snd(struct stream_interface *si);
 static void stream_int_update_conn(struct stream_interface *si);
+static int stream_int_shutr_conn(struct stream_interface *si);
+static int stream_int_shutw_conn(struct stream_interface *si);
 static void stream_int_chk_rcv_conn(struct stream_interface *si);
 static void stream_int_chk_snd_conn(struct stream_interface *si);
 static void si_conn_recv_cb(struct connection *conn);
@@ -51,6 +55,8 @@ struct si_ops si_embedded_ops = {
        .update  = stream_int_update_embedded,
        .chk_rcv = stream_int_chk_rcv,
        .chk_snd = stream_int_chk_snd,
+       .shutr   = stream_int_shutr,
+       .shutw   = stream_int_shutw,
 };
 
 /* stream-interface operations for connections */
@@ -58,6 +64,8 @@ struct si_ops si_conn_ops = {
        .update  = stream_int_update_conn,
        .chk_rcv = stream_int_chk_rcv_conn,
        .chk_snd = stream_int_chk_snd_conn,
+       .shutr   = stream_int_shutr_conn,
+       .shutw   = stream_int_shutw_conn,
 };
 
 struct data_cb si_conn_cb = {
@@ -200,22 +208,16 @@ static void stream_int_update_embedded(struct stream_interface *si)
 }
 
 /*
- * This function performs a shutdown-read on a stream interface in a connected
- * or init state (it does nothing for other states). It either shuts the read
- * side or marks itself as closed. The buffer flags are updated to reflect the
- * new state. If the stream interface has SI_FL_NOHALF, we also forward the
- * close to the write side. If a control layer is defined, then it is supposed
- * to be a socket layer and file descriptors are then shutdown or closed
- * accordingly. If no control layer is defined, then the SI is supposed to be
- * an embedded one and the owner task is woken up if it exists. The function
- * does not disable polling on the FD by itself, it returns non-zero instead
- * if the caller needs to do so (except when the FD is deleted where this is
- * implicit).
+ * This function performs a shutdown-read on a stream interface attached to an
+ * applet in a connected or init state (it does nothing for other states). It
+ * either shuts the read side or marks itself as closed. The buffer flags are
+ * updated to reflect the new state. If the stream interface has SI_FL_NOHALF,
+ * we also forward the close to the write side. The owner task is woken up if
+ * it exists. No control layer is supposed to be defined so this function
+ * always returns zero.
  */
-int stream_int_shutr(struct stream_interface *si)
+static int stream_int_shutr(struct stream_interface *si)
 {
-       struct connection *conn = si->conn;
-
        si->ib->flags &= ~CF_SHUTR_NOW;
        if (si->ib->flags & CF_SHUTR)
                return 0;
@@ -227,7 +229,6 @@ int stream_int_shutr(struct stream_interface *si)
                return 0;
 
        if (si->ob->flags & CF_SHUTW) {
-               conn_full_close(si->conn);
                si->state = SI_ST_DIS;
                si->exp = TICK_ETERNITY;
 
@@ -238,33 +239,24 @@ int stream_int_shutr(struct stream_interface *si)
                /* we want to immediately forward this close to the write side */
                return stream_int_shutw(si);
        }
-       else if (conn->ctrl) {
-               /* we want the caller to disable polling on this FD */
-               return 1;
-       }
 
        /* note that if the task exists, it must unregister itself once it runs */
-       if (!conn->ctrl && !(si->flags & SI_FL_DONT_WAKE) && si->owner)
+       if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
                task_wakeup(si->owner, TASK_WOKEN_IO);
        return 0;
 }
 
 /*
- * This function performs a shutdown-write on a stream interface in a connected or
- * init state (it does nothing for other states). It either shuts the write side
- * or marks itself as closed. The buffer flags are updated to reflect the new state.
- * It does also close everything if the SI was marked as being in error state. If
- * there is a data-layer shutdown, it is called. If a control layer is defined, then
- * it is supposed to be a socket layer and file descriptors are then shutdown or
- * closed accordingly. If no control layer is defined, then the SI is supposed to
- * be an embedded one and the owner task is woken up if it exists. The function
- * does not disable polling on the FD by itself, it returns non-zero instead if
- * the caller needs to do so (except when the FD is deleted where this is implicit).
+ * This function performs a shutdown-write on a stream interface attached to an
+ * applet in a connected or init state (it does nothing for other states). It
+ * either shuts the write side or marks itself as closed. The buffer flags are
+ * updated to reflect the new state. It does also close everything if the SI
+ * was marked as being in error state. The owner task is woken up if it exists.
+ * No control layer is supposed to be defined so this function always returns
+ * zero.
  */
-int stream_int_shutw(struct stream_interface *si)
+static int stream_int_shutw(struct stream_interface *si)
 {
-       struct connection *conn = si->conn;
-
        si->ob->flags &= ~CF_SHUTW_NOW;
        if (si->ob->flags & CF_SHUTW)
                return 0;
@@ -280,66 +272,30 @@ int stream_int_shutw(struct stream_interface *si)
                 * However, if SI_FL_NOLINGER is explicitly set, we know there is
                 * no risk so we close both sides immediately.
                 */
-               if (si->flags & SI_FL_ERR) {
-                       /* quick close, the socket is already shut. Remove pending flags. */
-                       si->flags &= ~SI_FL_NOLINGER;
-               } else if (si->flags & SI_FL_NOLINGER) {
-                       si->flags &= ~SI_FL_NOLINGER;
-                       if (conn->ctrl) {
-                               setsockopt(si->conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
-                                          (struct linger *) &nolinger, sizeof(struct linger));
-                       }
-                       /* unclean data-layer shutdown */
-                       if (conn->xprt && conn->xprt->shutw)
-                               conn->xprt->shutw(conn, 0);
-               } else {
-                       /* clean data-layer shutdown */
-                       if (conn->xprt && conn->xprt->shutw)
-                               conn->xprt->shutw(conn, 1);
-
-                       /* If the stream interface is configured to disable half-open
-                        * connections, we'll skip the shutdown(), but only if the
-                        * read size is already closed. Otherwise we can't support
-                        * closed write with pending read (eg: abortonclose while
-                        * waiting for the server).
-                        */
-                       if (!(si->flags & SI_FL_NOHALF) || !(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
-                               /* We shutdown transport layer */
-                               if (conn->ctrl)
-                                       shutdown(si->conn->t.sock.fd, SHUT_WR);
-
-                               if (!(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
-                                       /* OK just a shutw, but we want the caller
-                                        * to disable polling on this FD if exists.
-                                        */
-                                       return !!conn->ctrl;
-                               }
-                       }
-               }
+               if (!(si->flags & (SI_FL_ERR | SI_FL_NOLINGER)) &&
+                   !(si->ib->flags & (CF_SHUTR|CF_DONT_READ)))
+                       return 0;
 
                /* fall through */
        case SI_ST_CON:
-               /* we may have to close a pending connection, and mark the
-                * response buffer as shutr
-                */
-               conn_full_close(si->conn);
-               /* fall through */
        case SI_ST_CER:
        case SI_ST_QUE:
        case SI_ST_TAR:
+               /* Note that none of these states may happen with applets */
                si->state = SI_ST_DIS;
 
                if (si->release)
                        si->release(si);
        default:
-               si->flags &= ~SI_FL_WAIT_ROOM;
+               si->flags &= ~(SI_FL_WAIT_ROOM | SI_FL_NOLINGER);
+               si->ib->flags &= ~CF_SHUTR_NOW;
                si->ib->flags |= CF_SHUTR;
                si->ib->rex = TICK_ETERNITY;
                si->exp = TICK_ETERNITY;
        }
 
        /* note that if the task exists, it must unregister itself once it runs */
-       if (!conn->ctrl && !(si->flags & SI_FL_DONT_WAKE) && si->owner)
+       if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
                task_wakeup(si->owner, TASK_WOKEN_IO);
        return 0;
 }
@@ -530,7 +486,7 @@ static int si_conn_wake_cb(struct connection *conn)
        if (channel_is_empty(si->ob)) {
                if (((si->ob->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW) &&
                    (si->state == SI_ST_EST))
-                       stream_int_shutw(si);
+                       stream_int_shutw_conn(si);
                __conn_data_stop_send(conn);
                si->ob->wex = TICK_ETERNITY;
        }
@@ -756,6 +712,147 @@ void stream_int_update_conn(struct stream_interface *si)
        }
 }
 
+/*
+ * This function performs a shutdown-read on a stream interface attached to
+ * a connection in a connected or init state (it does nothing for other
+ * states). It either shuts the read side or marks itself as closed. The buffer
+ * flags are updated to reflect the new state. If the stream interface has
+ * SI_FL_NOHALF, we also forward the close to the write side. If a control
+ * layer is defined, then it is supposed to be a socket layer and file
+ * descriptors are then shutdown or closed accordingly. The function does not
+ * disable polling on the FD by itself, it returns non-zero instead if the
+ * caller needs to do so (except when the FD is deleted where this is
+ * implicit).
+ */
+static int stream_int_shutr_conn(struct stream_interface *si)
+{
+       struct connection *conn = si->conn;
+
+       si->ib->flags &= ~CF_SHUTR_NOW;
+       if (si->ib->flags & CF_SHUTR)
+               return 0;
+       si->ib->flags |= CF_SHUTR;
+       si->ib->rex = TICK_ETERNITY;
+       si->flags &= ~SI_FL_WAIT_ROOM;
+
+       if (si->state != SI_ST_EST && si->state != SI_ST_CON)
+               return 0;
+
+       if (si->ob->flags & CF_SHUTW) {
+               conn_full_close(si->conn);
+               si->state = SI_ST_DIS;
+               si->exp = TICK_ETERNITY;
+
+               if (si->release)
+                       si->release(si);
+       }
+       else if (si->flags & SI_FL_NOHALF) {
+               /* we want to immediately forward this close to the write side */
+               return stream_int_shutw_conn(si);
+       }
+       else if (conn->ctrl) {
+               /* we want the caller to disable polling on this FD */
+               return 1;
+       }
+
+       return 0;
+}
+
+/*
+ * This function performs a shutdown-write on a stream interface attached to
+ * a connection in a connected or init state (it does nothing for other
+ * states). It either shuts the write side or marks itself as closed. The
+ * buffer flags are updated to reflect the new state.  It does also close
+ * everything if the SI was marked as being in error state. If there is a
+ * data-layer shutdown, it is called. If a control layer is defined, then it is
+ * supposed to be a socket layer and file descriptors are then shutdown or
+ * closed accordingly. The function does not disable polling on the FD by
+ * itself, it returns non-zero instead if the caller needs to do so (except
+ * when the FD is deleted where this is implicit). Note: at the moment, we
+ * continue to check conn->ctrl eventhough we *know* it is valid. This will
+ * help selecting the proper shutdown() and setsockopt() calls if/when we
+ * implement remote sockets later.
+ */
+static int stream_int_shutw_conn(struct stream_interface *si)
+{
+       struct connection *conn = si->conn;
+
+       si->ob->flags &= ~CF_SHUTW_NOW;
+       if (si->ob->flags & CF_SHUTW)
+               return 0;
+       si->ob->flags |= CF_SHUTW;
+       si->ob->wex = TICK_ETERNITY;
+       si->flags &= ~SI_FL_WAIT_DATA;
+
+       switch (si->state) {
+       case SI_ST_EST:
+               /* we have to shut before closing, otherwise some short messages
+                * may never leave the system, especially when there are remaining
+                * unread data in the socket input buffer, or when nolinger is set.
+                * However, if SI_FL_NOLINGER is explicitly set, we know there is
+                * no risk so we close both sides immediately.
+                */
+               if (si->flags & SI_FL_ERR) {
+                       /* quick close, the socket is alredy shut anyway */
+               }
+               else if (si->flags & SI_FL_NOLINGER) {
+                       if (conn->ctrl) {
+                               setsockopt(si->conn->t.sock.fd, SOL_SOCKET, SO_LINGER,
+                                          (struct linger *) &nolinger, sizeof(struct linger));
+                       }
+                       /* unclean data-layer shutdown */
+                       if (conn->xprt && conn->xprt->shutw)
+                               conn->xprt->shutw(conn, 0);
+               }
+               else {
+                       /* clean data-layer shutdown */
+                       if (conn->xprt && conn->xprt->shutw)
+                               conn->xprt->shutw(conn, 1);
+
+                       /* If the stream interface is configured to disable half-open
+                        * connections, we'll skip the shutdown(), but only if the
+                        * read size is already closed. Otherwise we can't support
+                        * closed write with pending read (eg: abortonclose while
+                        * waiting for the server).
+                        */
+                       if (!(si->flags & SI_FL_NOHALF) || !(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
+                               /* We shutdown transport layer */
+                               if (conn->ctrl)
+                                       shutdown(si->conn->t.sock.fd, SHUT_WR);
+
+                               if (!(si->ib->flags & (CF_SHUTR|CF_DONT_READ))) {
+                                       /* OK just a shutw, but we want the caller
+                                        * to disable polling on this FD if exists.
+                                        */
+                                       return !!conn->ctrl;
+                               }
+                       }
+               }
+
+               /* fall through */
+       case SI_ST_CON:
+               /* we may have to close a pending connection, and mark the
+                * response buffer as shutr
+                */
+               conn_full_close(si->conn);
+               /* fall through */
+       case SI_ST_CER:
+       case SI_ST_QUE:
+       case SI_ST_TAR:
+               si->state = SI_ST_DIS;
+
+               if (si->release)
+                       si->release(si);
+       default:
+               si->flags &= ~(SI_FL_WAIT_ROOM | SI_FL_NOLINGER);
+               si->ib->flags &= ~CF_SHUTR_NOW;
+               si->ib->flags |= CF_SHUTR;
+               si->ib->rex = TICK_ETERNITY;
+               si->exp = TICK_ETERNITY;
+       }
+       return 0;
+}
+
 /* This function is used for inter-stream-interface calls. It is called by the
  * consumer to inform the producer side that it may be interested in checking
  * for free space in the buffer. Note that it intentionally does not update