]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: move the send-proxy code to tcp_connect_write()
authorWilly Tarreau <w@1wt.eu>
Sun, 20 May 2012 16:35:19 +0000 (18:35 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 20 May 2012 16:35:19 +0000 (18:35 +0200)
It is much better and more efficient to consider that the send-proxy
feature is part of the protocol layer than part of the data layer.
Now the connection is considered established once the send-proxy line
has been sent.

This way the data layer doesn't have to care anymore about this specific
part.

The tcp_connect_write() function now automatically calls the data layer
write() function once the connection is established, which saves calls
to epoll_ctl/epoll_wait/process_session.

It's starting to look more and more obvious that tcp_connect_read() and
tcp_connect_write() are not TCP-specific but only socket-specific and as
such should probably move, along with some functions from protocol.c, to
a socket-specific file (eg: stream_sock).

It would be nice to be able to support autonomous listeners to parse the
proxy protocol before accepting a connection, so that we get rid of it
at the session layer and to support using these informations in the
tcp-request connection rules.

src/proto_tcp.c
src/sock_raw.c

index 533e9e6c040c48a1a753a371b477643a471c620f..9931d28d7d615fb6f0f0e0fe906478c5958c5d08 100644 (file)
@@ -456,7 +456,7 @@ int tcp_connect_server(struct stream_interface *si)
         * connection is established before doing so, so we use our own write
         * callback then switch to the sock layer.
         */
-       if ((si->ob->flags & BF_OUT_EMPTY) && !si->send_proxy_ofs) {
+       if ((si->ob->flags & BF_OUT_EMPTY) || si->send_proxy_ofs) {
                fdtab[fd].cb[DIR_RD].f = tcp_connect_read;
                fdtab[fd].cb[DIR_WR].f = tcp_connect_write;
        }
@@ -515,56 +515,98 @@ int tcp_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir)
 
 /* This is the callback which is set when a connection establishment is pending
  * and we have nothing to send, or if we have an init function we want to call
- * once the connection is established.
+ * once the connection is established. It returns zero if it needs some polling
+ * before being called again.
  */
 static int tcp_connect_write(int fd)
 {
        struct stream_interface *si = fdtab[fd].owner;
        struct buffer *b = si->ob;
-       int retval = 1;
+       int retval = 0;
 
        if (fdtab[fd].state == FD_STERROR)
                goto out_error;
 
-       if (fdtab[fd].state != FD_STCONN) {
-               retval = 0;
+       if (fdtab[fd].state != FD_STCONN)
                goto out_ignore; /* strange we were called while ready */
-       }
 
        /* we might have been called just after an asynchronous shutw */
        if (b->flags & BF_SHUTW)
                goto out_wakeup;
 
-       /* We have no data to send to check the connection, and
-        * getsockopt() will not inform us whether the connection
-        * is still pending. So we'll reuse connect() to check the
-        * state of the socket. This has the advantage of giving us
-        * the following info :
-        *  - error
-        *  - connecting (EALREADY, EINPROGRESS)
-        *  - connected (EISCONN, 0)
+       /* If we have a PROXY line to send, we'll use this to validate the
+        * connection, in which case the connection is validated only once
+        * we've sent the whole proxy line. Otherwise we use connect().
         */
-       if ((connect(fd, fdinfo[fd].peeraddr, fdinfo[fd].peerlen) == 0))
-               errno = 0;
+       if (si->send_proxy_ofs) {
+               int ret;
 
-       if (errno == EALREADY || errno == EINPROGRESS) {
-               retval = 0;
-               goto out_ignore;
+               /* The target server expects a PROXY line to be sent first.
+                * If the send_proxy_ofs is negative, it corresponds to the
+                * offset to start sending from then end of the proxy string
+                * (which is recomputed every time since it's constant). If
+                * it is positive, it means we have to send from the start.
+                */
+               ret = make_proxy_line(trash, trashlen, &b->prod->addr.from, &b->prod->addr.to);
+               if (!ret)
+                       goto out_error;
+
+               if (si->send_proxy_ofs > 0)
+                       si->send_proxy_ofs = -ret; /* first call */
+
+               /* we have to send trash from (ret+sp for -sp bytes) */
+               ret = send(si->fd, trash + ret + si->send_proxy_ofs, -si->send_proxy_ofs,
+                          (b->flags & BF_OUT_EMPTY) ? 0 : MSG_MORE);
+
+               if (ret == 0)
+                       goto out_ignore;
+
+               if (ret < 0) {
+                       if (errno == EAGAIN)
+                               goto out_ignore;
+                       goto out_error;
+               }
+
+               si->send_proxy_ofs += ret; /* becomes zero once complete */
+               if (si->send_proxy_ofs != 0)
+                       goto out_ignore;
+
+               /* OK we've sent the whole line, we're connected */
        }
+       else {
+               /* We have no data to send to check the connection, and
+                * getsockopt() will not inform us whether the connection
+                * is still pending. So we'll reuse connect() to check the
+                * state of the socket. This has the advantage of giving us
+                * the following info :
+                *  - error
+                *  - connecting (EALREADY, EINPROGRESS)
+                *  - connected (EISCONN, 0)
+                */
+               if ((connect(fd, fdinfo[fd].peeraddr, fdinfo[fd].peerlen) < 0)) {
+                       if (errno == EALREADY || errno == EINPROGRESS)
+                               goto out_ignore;
 
-       if (errno && errno != EISCONN)
-               goto out_error;
+                       if (errno && errno != EISCONN)
+                               goto out_error;
+
+                       /* otherwise we're connected */
+               }
+       }
 
        /* OK we just need to indicate that we got a connection
         * and that we wrote nothing.
         */
        b->flags |= BF_WRITE_NULL;
 
-       /* The FD is ready now, we can hand the handlers to the socket layer */
+       /* The FD is ready now, we can hand the handlers to the socket layer
+        * and forward the event there to start working on the socket.
+        */
        fdtab[fd].cb[DIR_RD].f = si->sock.read;
        fdtab[fd].cb[DIR_WR].f = si->sock.write;
        fdtab[fd].state = FD_STREADY;
        si->exp = TICK_ETERNITY;
+       return si->sock.write(fd);
 
  out_wakeup:
        task_wakeup(si->owner, TASK_WOKEN_IO);
@@ -585,6 +627,7 @@ static int tcp_connect_write(int fd)
        fdtab[fd].ev &= ~FD_POLL_STICKY;
        EV_FD_REM(fd);
        si->flags |= SI_FL_ERR;
+       retval = 1;
        goto out_wakeup;
 }
 
index 37524be6d777c9463eac20a452dd4b5aa70b6269..af260274e3d57c82be228e8eea95b564ed8c685d 100644 (file)
@@ -32,7 +32,6 @@
 #include <proto/buffers.h>
 #include <proto/fd.h>
 #include <proto/freq_ctr.h>
-#include <proto/frontend.h>
 #include <proto/log.h>
 #include <proto/pipe.h>
 #include <proto/protocols.h>
@@ -524,43 +523,6 @@ static int sock_raw_write_loop(struct stream_interface *si, struct buffer *b)
        int retval = 1;
        int ret, max;
 
-       if (unlikely(si->send_proxy_ofs)) {
-               /* The target server expects a PROXY line to be sent first.
-                * If the send_proxy_ofs is negative, it corresponds to the
-                * offset to start sending from then end of the proxy string
-                * (which is recomputed every time since it's constant). If
-                * it is positive, it means we have to send from the start.
-                */
-               ret = make_proxy_line(trash, trashlen,
-                                     &b->prod->addr.from, &b->prod->addr.to);
-               if (!ret)
-                       return -1;
-
-               if (si->send_proxy_ofs > 0)
-                       si->send_proxy_ofs = -ret; /* first call */
-
-               /* we have to send trash from (ret+sp for -sp bytes) */
-               ret = send(si->fd, trash + ret + si->send_proxy_ofs, -si->send_proxy_ofs,
-                          (b->flags & BF_OUT_EMPTY) ? 0 : MSG_MORE);
-               if (ret > 0) {
-                       if (fdtab[si->fd].state == FD_STCONN) {
-                               fdtab[si->fd].state = FD_STREADY;
-                               si->exp = TICK_ETERNITY;
-                       }
-
-                       si->send_proxy_ofs += ret; /* becomes zero once complete */
-                       b->flags |= BF_WRITE_NULL; /* connect() succeeded */
-               }
-               else if (ret == 0 || errno == EAGAIN) {
-                       /* nothing written, we need to poll for write first */
-                       return 0;
-               }
-               else {
-                       /* bad, we got an error */
-                       return -1;
-               }
-       }
-
 #if defined(CONFIG_HAP_LINUX_SPLICE)
        while (b->pipe) {
                ret = splice(b->pipe->cons, NULL, si->fd, NULL, b->pipe->data,
@@ -723,8 +685,6 @@ static int sock_raw_write(int fd)
        retval = sock_raw_write_loop(si, b);
        if (retval < 0)
                goto out_error;
-       else if (retval == 0 && si->send_proxy_ofs)
-               goto out_may_wakeup; /* we failed to send the PROXY string */
 
        if (b->flags & BF_OUT_EMPTY) {
                /* the connection is established but we can't write. Either the
@@ -745,7 +705,6 @@ static int sock_raw_write(int fd)
                b->wex = TICK_ETERNITY;
        }
 
- out_may_wakeup:
        if (b->flags & BF_WRITE_ACTIVITY) {
                /* update timeout if we have written something */
                if ((b->flags & (BF_OUT_EMPTY|BF_SHUTW|BF_WRITE_PARTIAL)) == BF_WRITE_PARTIAL)
@@ -1033,7 +992,7 @@ static void sock_raw_chk_snd(struct stream_interface *si)
        if (unlikely(si->state != SI_ST_EST || (ob->flags & BF_SHUTW)))
                return;
 
-       if (unlikely((ob->flags & BF_OUT_EMPTY) && !(si->send_proxy_ofs)))  /* called with nothing to send ! */
+       if (unlikely(ob->flags & BF_OUT_EMPTY))  /* called with nothing to send ! */
                return;
 
        if (!ob->pipe &&                          /* spliced data wants to be forwarded ASAP */
@@ -1057,8 +1016,6 @@ static void sock_raw_chk_snd(struct stream_interface *si)
                si->flags |= SI_FL_ERR;
                goto out_wakeup;
        }
-       else if (retval == 0 && si->send_proxy_ofs)
-               goto out_may_wakeup; /* we failed to send the PROXY string */
 
        /* OK, so now we know that retval >= 0 means that some data might have
         * been sent, and that we may have to poll first. We have to do that
@@ -1091,7 +1048,6 @@ static void sock_raw_chk_snd(struct stream_interface *si)
                        ob->wex = tick_add_ifset(now_ms, ob->wto);
        }
 
- out_may_wakeup:
        if (likely(ob->flags & BF_WRITE_ACTIVITY)) {
                /* update timeout if we have written something */
                if ((ob->flags & (BF_OUT_EMPTY|BF_SHUTW|BF_WRITE_PARTIAL)) == BF_WRITE_PARTIAL)