From: Willy Tarreau Date: Mon, 23 Jul 2012 13:07:23 +0000 (+0200) Subject: MAJOR: tcp: remove the specific I/O callbacks for TCP connection probes X-Git-Tag: v1.5-dev12~109 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2da156fe5e01c4cc1e2f22bfb2a559da99494567;p=thirdparty%2Fhaproxy.git MAJOR: tcp: remove the specific I/O callbacks for TCP connection probes Use a single tcp_connect_probe() instead of tcp_connect_write() and tcp_connect_read(). We call this one only when no data layer function have been processed, so this is a fallback to test for completion of a connection attempt. With this done, we don't have the need for any direct I/O callback anymore. The function still relies on ->write() to wake the stream interface up, so it's not finished. --- diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h index c61fd86c97..66f29fcdbe 100644 --- a/include/proto/proto_tcp.h +++ b/include/proto/proto_tcp.h @@ -31,6 +31,7 @@ int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct so void tcpv4_add_listener(struct listener *listener); void tcpv6_add_listener(struct listener *listener); int tcp_connect_server(struct stream_interface *si); +int tcp_connect_probe(int fd); int tcp_get_src(int fd, struct sockaddr *sa, socklen_t salen, int dir); int tcp_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir); int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit); diff --git a/src/connection.c b/src/connection.c index c30e7395b6..679bc89444 100644 --- a/src/connection.c +++ b/src/connection.c @@ -15,6 +15,7 @@ #include +#include #include /* I/O callback for fd-based connections. It calls the read/write handlers @@ -46,6 +47,18 @@ int conn_fd_handler(int fd) if (fdtab[fd].ev & (FD_POLL_OUT | FD_POLL_ERR)) if (!conn->data->write(fd)) ret |= FD_WAIT_WRITE; + + if (conn->flags & CO_FL_ERROR) + goto leave; + + if (conn->flags & CO_FL_WAIT_L4_CONN) { + /* still waiting for a connection to establish and no data to + * send in order to probe it ? Then let's retry the connect(). + */ + if (!tcp_connect_probe(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); diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 86ef47b7cf..db2fe03105 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -61,8 +61,6 @@ static int tcp_bind_listeners(struct protocol *proto, char *errmsg, int errlen); static int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen); -static int tcp_connect_write(int fd); -static int tcp_connect_read(int fd); /* Note: must not be declared as its list will be overwritten */ static struct protocol proto_tcpv4 = { @@ -480,10 +478,6 @@ int tcp_connect_server(struct stream_interface *si) 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; - } fdtab[fd].iocb = conn_fd_handler; fd_insert(fd); @@ -536,7 +530,7 @@ int tcp_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir) * once the connection is established. It returns zero if it needs some polling * before being called again. */ -static int tcp_connect_write(int fd) +int tcp_connect_probe(int fd) { struct connection *conn = fdtab[fd].owner; struct stream_interface *si = container_of(conn, struct stream_interface, conn); @@ -549,6 +543,10 @@ static int tcp_connect_write(int fd) if (!(conn->flags & CO_FL_WAIT_L4_CONN)) goto out_ignore; /* strange we were called while ready */ + /* stop here if we reached the end of data */ + if ((fdtab[fd].ev & (FD_POLL_IN|FD_POLL_HUP)) == FD_POLL_HUP) + goto out_error; + /* we might have been called just after an asynchronous shutw */ if (b->flags & BF_SHUTW) goto out_wakeup; @@ -609,49 +607,6 @@ static int tcp_connect_write(int fd) } -/* might be used on connect error */ -static int tcp_connect_read(int fd) -{ - struct connection *conn = fdtab[fd].owner; - struct stream_interface *si = container_of(conn, struct stream_interface, conn); - int retval; - - retval = 1; - - if (conn->flags & CO_FL_ERROR) - goto out_error; - - if (!(conn->flags & CO_FL_WAIT_L4_CONN)) { - retval = 0; - goto out_ignore; /* strange we were called while ready */ - } - - /* stop here if we reached the end of data */ - if ((fdtab[fd].ev & (FD_POLL_IN|FD_POLL_HUP)) == FD_POLL_HUP) - goto out_error; - - out_wakeup: - task_wakeup(si->owner, TASK_WOKEN_IO); - out_ignore: - fdtab[fd].ev &= ~FD_POLL_IN; - return retval; - - out_error: - /* Read 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); - si->flags |= SI_FL_ERR; - goto out_wakeup; -} - - /* This function tries to bind a TCPv4/v6 listener. It may return a warning or * an error message in if the message is at most bytes long * (including '\0'). The return value is composed from ERR_ABORT, ERR_WARN,