From a190d591fcb9d3db8dfe5905bcd879770f473c5b Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 20 May 2012 18:35:19 +0200 Subject: [PATCH] REORG: move the send-proxy code to tcp_connect_write() 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 | 87 ++++++++++++++++++++++++++++++++++++------------- src/sock_raw.c | 46 +------------------------- 2 files changed, 66 insertions(+), 67 deletions(-) diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 533e9e6c04..9931d28d7d 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -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; } diff --git a/src/sock_raw.c b/src/sock_raw.c index 37524be6d7..af260274e3 100644 --- a/src/sock_raw.c +++ b/src/sock_raw.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -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) -- 2.47.3