]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: connection: extract the send_proxy callback from proto_tcp
authorWilly Tarreau <wtarreau@exceliance.fr>
Fri, 6 Jul 2012 15:12:34 +0000 (17:12 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 2 Sep 2012 19:51:28 +0000 (21:51 +0200)
This handshake handler must be independant, so move it away from
proto_tcp. It has a dedicated connection flag. It is tested before
I/O handlers and automatically removes the CO_FL_WAIT_L4_CONN flag
upon success.

It also sets the BF_WRITE_NULL flag on the stream interface and
stops the SI timeout. However it does not perform the task_wakeup(),
and relies on the data handler to do so for now. The SI wakeup will
have to be moved elsewhere anyway.

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

index 7f1cb9e97d79e32cc76dc4139269b1bc497b13d4..f36af89662c125789fc6863fe7fcebd6a1335b75 100644 (file)
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 
 #include <common/config.h>
+#include <types/session.h>
 #include <types/stream_interface.h>
 
 
@@ -32,6 +33,7 @@
 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);
 
 extern struct sock_ops stream_int_embedded;
 extern struct sock_ops stream_int_task;
index 94c8410c3c2cbf8c31c4a199dcf11141b2902015..6e0b54510102bd18333a82b88a39fefa9b8b4ba8 100644 (file)
@@ -36,6 +36,8 @@ enum {
        CO_FL_NONE          = 0x00000000,
        CO_FL_ERROR         = 0x00000001,  /* a fatal error was reported     */
        CO_FL_WAIT_L4_CONN  = 0x00000002,  /* waiting for L4 to be connected */
+       /* flags below are used for connection handshakes */
+       CO_FL_SI_SEND_PROXY = 0x00000004,  /* send a valid PROXY protocol header */
 };
 
 /* This structure describes a connection with its methods and data.
index 089ad892558cff2210019da3e574ed65b7714e07..c30e7395b6175a035c84fc729ba14c777965ccb8 100644 (file)
@@ -14,7 +14,8 @@
 #include <common/config.h>
 
 #include <types/connection.h>
-#include <types/stream_interface.h>
+
+#include <proto/stream_interface.h>
 
 /* I/O callback for fd-based connections. It calls the read/write handlers
  * provided by the connection's sock_ops, which must be valid. It returns
@@ -26,22 +27,26 @@ int conn_fd_handler(int fd)
        int ret = 0;
 
        if (!conn)
-               return ret;
+               goto leave;
 
        if (conn->flags & CO_FL_ERROR)
-               return ret;
+               goto leave;
+
+       if (conn->flags & CO_FL_SI_SEND_PROXY)
+               if ((ret = conn_si_send_proxy(conn, CO_FL_SI_SEND_PROXY)))
+                       goto leave;
 
        if (fdtab[fd].ev & (FD_POLL_IN | FD_POLL_HUP | FD_POLL_ERR))
                if (!conn->data->read(fd))
                        ret |= FD_WAIT_READ;
 
        if (conn->flags & CO_FL_ERROR)
-               return ret;
+               goto leave;
 
        if (fdtab[fd].ev & (FD_POLL_OUT | FD_POLL_ERR))
                if (!conn->data->write(fd))
                        ret |= FD_WAIT_WRITE;
-
+ leave:
        /* remove the events before leaving */
        fdtab[fd].ev &= ~(FD_POLL_IN | FD_POLL_OUT | FD_POLL_HUP | FD_POLL_ERR);
        return ret;
index 87ac849ce631acdab1a3da364eeeb78f8054142b..86ef47b7cfd4479d082859bff3e8c776aab1d6de 100644 (file)
@@ -41,7 +41,7 @@
 #include <proto/arg.h>
 #include <proto/buffers.h>
 #include <proto/connection.h>
-#include <proto/frontend.h>
+//#include <proto/frontend.h>
 #include <proto/log.h>
 #include <proto/port_range.h>
 #include <proto/protocols.h>
@@ -470,18 +470,20 @@ int tcp_connect_server(struct stream_interface *si)
        fdtab[fd].flags = FD_FL_TCP | FD_FL_TCP_NODELAY;
        si->conn.flags  = CO_FL_WAIT_L4_CONN; /* connection in progress */
 
-       /* If we have nothing to send, we want to confirm that the TCP
+       /* Prepare to send a few handshakes related to the on-wire protocol.
+        * If we have nothing to send, we want to confirm that the TCP
         * 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) {
+       fdtab[fd].cb[DIR_RD].f = NULL;
+       fdtab[fd].cb[DIR_WR].f = NULL;
+
+       if (si->send_proxy_ofs)
+               si->conn.flags |= CO_FL_SI_SEND_PROXY;
+       else if (si->ob->flags & BF_OUT_EMPTY) {
                fdtab[fd].cb[DIR_RD].f = tcp_connect_read;
                fdtab[fd].cb[DIR_WR].f = tcp_connect_write;
        }
-       else {
-               fdtab[fd].cb[DIR_RD].f = NULL;
-               fdtab[fd].cb[DIR_WR].f = NULL;
-       }
 
        fdtab[fd].iocb = conn_fd_handler;
        fd_insert(fd);
@@ -551,64 +553,23 @@ static int tcp_connect_write(int fd)
        if (b->flags & BF_SHUTW)
                goto out_wakeup;
 
-       /* 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().
+       /* 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 (si->send_proxy_ofs) {
-               int ret;
-
-               /* 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(fd, trash + ret + si->send_proxy_ofs, -si->send_proxy_ofs,
-                          (b->flags & BF_OUT_EMPTY) ? 0 : MSG_MORE);
-
-               if (ret == 0)
+       if ((connect(fd, conn->peeraddr, conn->peerlen) < 0)) {
+               if (errno == EALREADY || errno == EINPROGRESS)
                        goto out_ignore;
 
-               if (ret < 0) {
-                       if (errno == EAGAIN)
-                               goto out_ignore;
+               if (errno && errno != EISCONN)
                        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, conn->peeraddr, conn->peerlen) < 0)) {
-                       if (errno == EALREADY || errno == EINPROGRESS)
-                               goto out_ignore;
 
-                       if (errno && errno != EISCONN)
-                               goto out_error;
-
-                       /* otherwise we're connected */
-               }
+               /* otherwise we're connected */
        }
 
        /* OK we just need to indicate that we got a connection
@@ -629,7 +590,6 @@ static int tcp_connect_write(int fd)
        task_wakeup(si->owner, TASK_WOKEN_IO);
 
  out_ignore:
-       fdtab[fd].ev &= ~FD_POLL_OUT;
        return retval;
 
  out_error:
index 5f7ac3dc6e2e56372540b5cf6626c8af0719c54e..59eca1bd3d341a8ac595553163cf5d4f820daaa3 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <proto/buffers.h>
 #include <proto/fd.h>
+#include <proto/frontend.h>
 #include <proto/sock_raw.h>
 #include <proto/stream_interface.h>
 #include <proto/task.h>
@@ -401,6 +402,91 @@ void stream_int_unregister_handler(struct stream_interface *si)
        clear_target(&si->target);
 }
 
+/* This callback is used to send a valid PROXY protocol line to a socket being
+ * established. It returns a combination of FD_WAIT_* if it wants some polling
+ * before being called again, otherwise it returns zero and removes itself from
+ * the connection's flags (the bit is provided in <flag> by the caller).
+ */
+int conn_si_send_proxy(struct connection *conn, unsigned int flag)
+{
+       int fd = conn->t.sock.fd;
+       struct stream_interface *si = container_of(conn, struct stream_interface, conn);
+       struct buffer *b = si->ob;
+
+       /* we might have been called just after an asynchronous shutw */
+       if (b->flags & BF_SHUTW)
+               goto out_error;
+
+       /* 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 (si->send_proxy_ofs) {
+               int ret;
+
+               /* 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(fd, trash + ret + si->send_proxy_ofs, -si->send_proxy_ofs,
+                          (b->flags & BF_OUT_EMPTY) ? 0 : MSG_MORE);
+
+               if (ret == 0)
+                       goto out_wait;
+
+               if (ret < 0) {
+                       if (errno == EAGAIN)
+                               goto out_wait;
+                       goto out_error;
+               }
+
+               si->send_proxy_ofs += ret; /* becomes zero once complete */
+               if (si->send_proxy_ofs != 0)
+                       goto out_wait;
+
+               /* OK we've sent the whole line, we're connected */
+       }
+
+       /* The FD is ready now, simply return and let the connection handler
+        * notify upper layers if needed.
+        */
+       if (conn->flags & CO_FL_WAIT_L4_CONN)
+               conn->flags &= ~CO_FL_WAIT_L4_CONN;
+       b->flags |= BF_WRITE_NULL;
+       si->exp = TICK_ETERNITY;
+
+ out_leave:
+       conn->flags &= ~flag;
+       return 0;
+
+ out_error:
+       /* Write error on the file descriptor. We mark the FD as STERROR so
+        * that we don't use it anymore. The error is reported to the stream
+        * interface which will take proper action. We must not perturbate the
+        * buffer because the stream interface wants to ensure transparent
+        * connection retries.
+        */
+
+       conn->flags |= CO_FL_ERROR;
+       fdtab[fd].ev &= ~FD_POLL_STICKY;
+       EV_FD_REM(fd);
+       goto out_leave;
+
+ out_wait:
+       return FD_WAIT_WRITE;
+}
+
+
 /*
  * Local variables:
  *  c-indent-level: 8