]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
OPTIM/MEDIUM: stream_interface: add a new SI_FL_NOHALF flag
authorWilly Tarreau <w@1wt.eu>
Sun, 13 May 2012 12:48:59 +0000 (14:48 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 13 May 2012 12:52:22 +0000 (14:52 +0200)
This flag indicates that we're not interested in keeping half-open
connections on a stream interface. It has the benefit of allowing
the socket layer to cause an immediate write close when detecting
an incoming read close. This releases resources much faster and
saves one syscall (either a shutdown or setsockopt).

This flag is only set by HTTP on the interface going to the server
since we don't want to continue pushing data there when it has
closed.

Another benefit is that it responds with a FIN to a server's FIN
instead of responding with an RST as it used to, which is much
cleaner.

Performance gains of 7.5% have been measured on HTTP connection
rate on empty objects.

include/types/stream_interface.h
src/proto_http.c
src/session.c
src/sock_raw.c

index fb89f50d46462652144de3508a276f69e525e990..dde7b3fe5a58e4c6d93671d18017679eef5ff920 100644 (file)
@@ -72,6 +72,7 @@ enum {
        SI_FL_DONT_WAKE  = 0x0020,  /* resync in progress, don't wake up */
        SI_FL_INDEP_STR  = 0x0040,  /* independant streams = don't update rex on write */
        SI_FL_NOLINGER   = 0x0080,  /* may close without lingering. One-shot. */
+       SI_FL_NOHALF     = 0x0100,  /* no half close, close both sides at once */
        SI_FL_SRC_ADDR   = 0x1000,  /* get the source ip/port with getsockname */
        SI_FL_TO_SET     = 0x2000,  /* addr.to is set */
        SI_FL_FROM_SET   = 0x4000,  /* addr.from is set */
index 18db42f74851c595188ccbfcc6359da8847c0cda..f18ae724de59931909d263064f019c7b40f52f28 100644 (file)
@@ -3424,6 +3424,11 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit)
        req->analyse_exp = TICK_ETERNITY;
        req->analysers &= ~an_bit;
 
+       /* if the server closes the connection, we want to immediately react
+        * and close the socket to save packets and syscalls.
+        */
+       req->cons->flags |= SI_FL_NOHALF;
+
        s->logs.tv_request = now;
        /* OK let's go on with the BODY now */
        return 1;
@@ -3670,7 +3675,7 @@ void http_end_txn_clean_session(struct session *s)
         */
        http_silent_debug(__LINE__, s);
 
-       s->req->cons->flags |= SI_FL_NOLINGER;
+       s->req->cons->flags |= SI_FL_NOLINGER | SI_FL_NOHALF;
        s->req->cons->sock.shutr(s->req->cons);
        s->req->cons->sock.shutw(s->req->cons);
 
index 036480c837d8d20fe6e4315ce9719f3d33956256..a3becfebd47cdee74656d16c58048828a6440a1a 100644 (file)
@@ -1359,8 +1359,11 @@ struct task *process_session(struct task *t)
                        s->req->cons->sock.shutw(s->req->cons);
                }
 
-               if (unlikely((s->req->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT))
+               if (unlikely((s->req->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT)) {
+                       if (s->req->prod->flags & SI_FL_NOHALF)
+                               s->req->prod->flags |= SI_FL_NOLINGER;
                        s->req->prod->sock.shutr(s->req->prod);
+               }
 
                buffer_check_timeouts(s->rep);
 
@@ -1369,8 +1372,11 @@ struct task *process_session(struct task *t)
                        s->rep->cons->sock.shutw(s->rep->cons);
                }
 
-               if (unlikely((s->rep->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT))
+               if (unlikely((s->rep->flags & (BF_SHUTR|BF_READ_TIMEOUT)) == BF_READ_TIMEOUT)) {
+                       if (s->rep->prod->flags & SI_FL_NOHALF)
+                               s->rep->prod->flags |= SI_FL_NOLINGER;
                        s->rep->prod->sock.shutr(s->rep->prod);
+               }
        }
 
        /* 1b: check for low-level errors reported at the stream interface.
@@ -1907,8 +1913,11 @@ struct task *process_session(struct task *t)
                buffer_shutr_now(s->req);
 
        /* shutdown(read) pending */
-       if (unlikely((s->req->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW))
+       if (unlikely((s->req->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW)) {
+               if (s->req->prod->flags & SI_FL_NOHALF)
+                       s->req->prod->flags |= SI_FL_NOLINGER;
                s->req->prod->sock.shutr(s->req->prod);
+       }
 
        /* it's possible that an upper layer has requested a connection setup or abort.
         * There are 2 situations where we decide to establish a new connection :
@@ -2049,8 +2058,11 @@ struct task *process_session(struct task *t)
                buffer_shutr_now(s->rep);
 
        /* shutdown(read) pending */
-       if (unlikely((s->rep->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW))
+       if (unlikely((s->rep->flags & (BF_SHUTR|BF_SHUTR_NOW)) == BF_SHUTR_NOW)) {
+               if (s->rep->prod->flags & SI_FL_NOHALF)
+                       s->rep->prod->flags |= SI_FL_NOLINGER;
                s->rep->prod->sock.shutr(s->rep->prod);
+       }
 
        if (s->req->prod->state == SI_ST_DIS || s->req->cons->state == SI_ST_DIS)
                goto resync_stream_interface;
index 7fba926f0aebd663512696047e95f5d9c6aa097c..b7edbfc977683435f1f48badc3dda4a74fe48ba9 100644 (file)
@@ -819,11 +819,13 @@ static void sock_raw_shutw(struct stream_interface *si)
                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) {
+               }
+               else if (si->flags & SI_FL_NOLINGER) {
                        si->flags &= ~SI_FL_NOLINGER;
                        setsockopt(si->fd, SOL_SOCKET, SO_LINGER,
                                   (struct linger *) &nolinger, sizeof(struct linger));
-               } else {
+               }
+               else if (!(si->flags & SI_FL_NOHALF)) {
                        EV_FD_CLR(si->fd, DIR_WR);
                        shutdown(si->fd, SHUT_WR);
 
@@ -857,7 +859,8 @@ static void sock_raw_shutw(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 closes the file descriptor and marks itself as closed. The buffer flags are
- * updated to reflect the new state.
+ * updated to reflect the new state. If the stream interface has SI_FL_NOHALF,
+ * we also forward the close to the write side.
  */
 static void sock_raw_shutr(struct stream_interface *si)
 {
@@ -880,6 +883,10 @@ static void sock_raw_shutr(struct stream_interface *si)
                        si->release(si);
                return;
        }
+       else if (si->flags & SI_FL_NOHALF) {
+               /* we want to immediately forward this close to the write side */
+               return sock_raw_shutw(si);
+       }
        EV_FD_CLR(si->fd, DIR_RD);
        return;
 }