mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-01-29 14:01:50 +01:00
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.
This commit is contained in:
parent
8ae52cb144
commit
a190d591fc
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user