]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] move connection establishment from backend to the SI.
authorWilly Tarreau <w@1wt.eu>
Sun, 16 Aug 2009 12:02:45 +0000 (14:02 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 16 Aug 2009 15:46:15 +0000 (17:46 +0200)
The connection establishment was completely handled by backend.c which
normally just handles LB algos. Since it's purely TCP, it must move to
proto_tcp.c. Also, instead of calling it directly, we now call it via
the stream interface, which will later help us unify session handling.

include/proto/proto_tcp.h
include/types/fd.h
include/types/stream_interface.h
src/backend.c
src/client.c
src/proto_tcp.c
src/proto_uxst.c

index 2cf3be18c1f328ca063a1c4c5ba6e606763f0611..b93b3714567deccab9fa72b656acd62047766406 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <common/config.h>
 #include <types/proto_tcp.h>
-#include <types/session.h>
 #include <types/task.h>
 
 int tcp_event_accept(int fd);
@@ -32,6 +31,9 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct socka
 void tcpv4_add_listener(struct listener *listener);
 void tcpv6_add_listener(struct listener *listener);
 int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen);
+int tcpv4_connect_server(struct stream_interface *si,
+                        struct proxy *be, struct server *srv,
+                        struct sockaddr *srv_addr, struct sockaddr *cli_addr);
 int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit);
 int acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
                          struct acl_expr *expr, struct acl_test *test);
index 0c631b1d62cec775ccb4da16b117db7a318ae56d..a50d076c3771500085c8dca56d618116a603f3e6 100644 (file)
@@ -29,7 +29,6 @@
 
 #include <common/config.h>
 #include <types/task.h>
-#include <types/buffers.h>
 #include <types/protocols.h>
 
 /* different possible states for the fd */
index f97aa624da0928320b4119b0cbb1dc7331933f50..7789323fdbc19b6d63461bc6b9591958475b49b9 100644 (file)
@@ -72,6 +72,9 @@ enum {
 
 #define SI_FL_CAP_SPLICE (SI_FL_CAP_SPLTCP)
 
+struct server;
+struct proxy;
+
 struct stream_interface {
        unsigned int state;     /* SI_ST* */
        unsigned int prev_state;/* SI_ST*, copy of previous state */
@@ -79,6 +82,8 @@ struct stream_interface {
        int fd;                 /* file descriptor for a stream driver when known */
        unsigned int flags;
        unsigned int exp;       /* wake up time for connect, queue, turn-around, ... */
+       int (*connect)(struct stream_interface *, struct proxy *, struct server *,
+                      struct sockaddr *, struct sockaddr *); /* connect function if any */
        void (*shutr)(struct stream_interface *);  /* shutr function */
        void (*shutw)(struct stream_interface *);  /* shutw function */
        void (*chk_rcv)(struct stream_interface *);/* chk_rcv function */
index baa301a552a1f596e15bc3cbba4022794ace2e59..7a6b7b8f4a45c3019e04a96f8d57b32df7162540 100644 (file)
@@ -18,8 +18,6 @@
 #include <string.h>
 #include <ctype.h>
 
-#include <netinet/tcp.h>
-
 #include <common/compat.h>
 #include <common/config.h>
 #include <common/debug.h>
 #include <proto/acl.h>
 #include <proto/backend.h>
 #include <proto/client.h>
-#include <proto/fd.h>
-#include <proto/httperr.h>
-#include <proto/log.h>
-#include <proto/port_range.h>
 #include <proto/proto_http.h>
 #include <proto/proto_tcp.h>
 #include <proto/queue.h>
 #include <proto/server.h>
 #include <proto/session.h>
-#include <proto/stream_sock.h>
 #include <proto/task.h>
 
 static inline void fwrr_remove_from_tree(struct server *s);
@@ -1791,7 +1784,7 @@ int assign_server_and_queue(struct session *s)
  */
 int connect_server(struct session *s)
 {
-       int fd, err;
+       int err;
 
        if (!(s->flags & SN_ADDR_SET)) {
                err = assign_server_address(s);
@@ -1799,234 +1792,16 @@ int connect_server(struct session *s)
                        return SN_ERR_INTERNAL;
        }
 
-       if ((fd = s->req->cons->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-               qfprintf(stderr, "Cannot get a server socket.\n");
-
-               if (errno == ENFILE)
-                       send_log(s->be, LOG_EMERG,
-                                "Proxy %s reached system FD limit at %d. Please check system tunables.\n",
-                                s->be->id, maxfd);
-               else if (errno == EMFILE)
-                       send_log(s->be, LOG_EMERG,
-                                "Proxy %s reached process FD limit at %d. Please check 'ulimit-n' and restart.\n",
-                                s->be->id, maxfd);
-               else if (errno == ENOBUFS || errno == ENOMEM)
-                       send_log(s->be, LOG_EMERG,
-                                "Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n",
-                                s->be->id, maxfd);
-               /* this is a resource error */
-               return SN_ERR_RESOURCE;
-       }
-
-       if (fd >= global.maxsock) {
-               /* do not log anything there, it's a normal condition when this option
-                * is used to serialize connections to a server !
-                */
-               Alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
-               close(fd);
-               return SN_ERR_PRXCOND; /* it is a configuration limit */
-       }
-
-       if ((fcntl(fd, F_SETFL, O_NONBLOCK)==-1) ||
-           (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) == -1)) {
-               qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
-               close(fd);
+       if (!s->req->cons->connect)
                return SN_ERR_INTERNAL;
-       }
-
-       if (s->be->options & PR_O_TCP_SRV_KA)
-               setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
-
-       if (s->be->options & PR_O_TCP_NOLING)
-               setsockopt(fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger));
-
-       /* allow specific binding :
-        * - server-specific at first
-        * - proxy-specific next
-        */
-       if (s->srv != NULL && s->srv->state & SRV_BIND_SRC) {
-               struct sockaddr_in *remote = NULL;
-               int ret, flags = 0;
-
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
-               switch (s->srv->state & SRV_TPROXY_MASK) {
-               case SRV_TPROXY_ADDR:
-                       remote = (struct sockaddr_in *)&s->srv->tproxy_addr;
-                       flags  = 3;
-                       break;
-               case SRV_TPROXY_CLI:
-                       flags |= 2;
-                       /* fall through */
-               case SRV_TPROXY_CIP:
-                       /* FIXME: what can we do if the client connects in IPv6 ? */
-                       flags |= 1;
-                       remote = (struct sockaddr_in *)&s->cli_addr;
-                       break;
-               }
-#endif
-#ifdef SO_BINDTODEVICE
-               /* Note: this might fail if not CAP_NET_RAW */
-               if (s->srv->iface_name)
-                       setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, s->srv->iface_name, s->srv->iface_len + 1);
-#endif
-
-               if (s->srv->sport_range) {
-                       int attempts = 10; /* should be more than enough to find a spare port */
-                       struct sockaddr_in src;
-
-                       ret = 1;
-                       src = s->srv->source_addr;
-
-                       do {
-                               /* note: in case of retry, we may have to release a previously
-                                * allocated port, hence this loop's construct.
-                                */
-                               port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port);
-                               fdtab[fd].port_range = NULL;
-
-                               if (!attempts)
-                                       break;
-                               attempts--;
-
-                               fdtab[fd].local_port = port_range_alloc_port(s->srv->sport_range);
-                               if (!fdtab[fd].local_port)
-                                       break;
-
-                               fdtab[fd].port_range = s->srv->sport_range;
-                               src.sin_port = htons(fdtab[fd].local_port);
-
-                               ret = tcpv4_bind_socket(fd, flags, &src, remote);
-                       } while (ret != 0); /* binding NOK */
-               }
-               else {
-                       ret = tcpv4_bind_socket(fd, flags, &s->srv->source_addr, remote);
-               }
 
-               if (ret) {
-                       port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port);
-                       fdtab[fd].port_range = NULL;
-                       close(fd);
-
-                       if (ret == 1) {
-                               Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n",
-                                     s->be->id, s->srv->id);
-                               send_log(s->be, LOG_EMERG,
-                                        "Cannot bind to source address before connect() for server %s/%s.\n",
-                                        s->be->id, s->srv->id);
-                       } else {
-                               Alert("Cannot bind to tproxy source address before connect() for server %s/%s. Aborting.\n",
-                                     s->be->id, s->srv->id);
-                               send_log(s->be, LOG_EMERG,
-                                        "Cannot bind to tproxy source address before connect() for server %s/%s.\n",
-                                        s->be->id, s->srv->id);
-                       }
-                       return SN_ERR_RESOURCE;
-               }
-       }
-       else if (s->be->options & PR_O_BIND_SRC) {
-               struct sockaddr_in *remote = NULL;
-               int ret, flags = 0;
+       err = s->req->cons->connect(s->req->cons, s->be, s->srv,
+                                   (struct sockaddr *)&s->srv_addr,
+                                   (struct sockaddr *)&s->cli_addr);
 
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
-               switch (s->be->options & PR_O_TPXY_MASK) {
-               case PR_O_TPXY_ADDR:
-                       remote = (struct sockaddr_in *)&s->be->tproxy_addr;
-                       flags  = 3;
-                       break;
-               case PR_O_TPXY_CLI:
-                       flags |= 2;
-                       /* fall through */
-               case PR_O_TPXY_CIP:
-                       /* FIXME: what can we do if the client connects in IPv6 ? */
-                       flags |= 1;
-                       remote = (struct sockaddr_in *)&s->cli_addr;
-                       break;
-               }
-#endif
-#ifdef SO_BINDTODEVICE
-               /* Note: this might fail if not CAP_NET_RAW */
-               if (s->be->iface_name)
-                       setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, s->be->iface_name, s->be->iface_len + 1);
-#endif
-               ret = tcpv4_bind_socket(fd, flags, &s->be->source_addr, remote);
-               if (ret) {
-                       close(fd);
-                       if (ret == 1) {
-                               Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n",
-                                     s->be->id);
-                               send_log(s->be, LOG_EMERG,
-                                        "Cannot bind to source address before connect() for proxy %s.\n",
-                                        s->be->id);
-                       } else {
-                               Alert("Cannot bind to tproxy source address before connect() for proxy %s. Aborting.\n",
-                                     s->be->id);
-                               send_log(s->be, LOG_EMERG,
-                                        "Cannot bind to tproxy source address before connect() for proxy %s.\n",
-                                        s->be->id);
-                       }
-                       return SN_ERR_RESOURCE;
-               }
-       }
-
-#if defined(TCP_QUICKACK) && defined(SOL_TCP)
-       /* disabling tcp quick ack now allows the first request to leave the
-        * machine with the first ACK. We only do this if there are pending
-        * data in the buffer.
-        */
-       if ((s->be->options2 & PR_O2_SMARTCON) && s->req->send_max)
-                setsockopt(fd, SOL_TCP, TCP_QUICKACK, (char *) &zero, sizeof(zero));
-#endif
-
-       if ((connect(fd, (struct sockaddr *)&s->srv_addr, sizeof(s->srv_addr)) == -1) &&
-           (errno != EINPROGRESS) && (errno != EALREADY) && (errno != EISCONN)) {
-
-               if (errno == EAGAIN || errno == EADDRINUSE) {
-                       char *msg;
-                       if (errno == EAGAIN) /* no free ports left, try again later */
-                               msg = "no free ports";
-                       else
-                               msg = "local address already in use";
-
-                       qfprintf(stderr,"Cannot connect: %s.\n",msg);
-                       port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port);
-                       fdtab[fd].port_range = NULL;
-                       close(fd);
-                       send_log(s->be, LOG_EMERG,
-                                "Connect() failed for server %s/%s: %s.\n",
-                                s->be->id, s->srv->id, msg);
-                       return SN_ERR_RESOURCE;
-               } else if (errno == ETIMEDOUT) {
-                       //qfprintf(stderr,"Connect(): ETIMEDOUT");
-                       port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port);
-                       fdtab[fd].port_range = NULL;
-                       close(fd);
-                       return SN_ERR_SRVTO;
-               } else {
-                       // (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM)
-                       //qfprintf(stderr,"Connect(): %d", errno);
-                       port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port);
-                       fdtab[fd].port_range = NULL;
-                       close(fd);
-                       return SN_ERR_SRVCL;
-               }
-       }
-
-       fdtab[fd].owner = s->req->cons;
-       fdtab[fd].state = FD_STCONN; /* connection in progress */
-       fdtab[fd].flags = FD_FL_TCP | FD_FL_TCP_NODELAY;
-       fdtab[fd].cb[DIR_RD].f = &stream_sock_read;
-       fdtab[fd].cb[DIR_RD].b = s->rep;
-       fdtab[fd].cb[DIR_WR].f = &stream_sock_write;
-       fdtab[fd].cb[DIR_WR].b = s->req;
-
-       fdtab[fd].peeraddr = (struct sockaddr *)&s->srv_addr;
-       fdtab[fd].peerlen = sizeof(s->srv_addr);
-
-       fd_insert(fd);
-       EV_FD_SET(fd, DIR_WR);  /* for connect status */
+       if (err != SN_ERR_NONE)
+               return err;
 
-       s->req->cons->state = SI_ST_CON;
-       s->req->cons->flags |= SI_FL_CAP_SPLTCP; /* TCP supports splicing */
        if (s->srv) {
                s->flags |= SN_CURR_SESS;
                s->srv->cur_sess++;
@@ -2036,7 +1811,6 @@ int connect_server(struct session *s)
                        s->be->lbprm.server_take_conn(s->srv);
        }
 
-       s->req->cons->exp = tick_add_ifset(now_ms, s->be->timeout.connect);
        return SN_ERR_NONE;  /* connection is OK */
 }
 
index 6210cdbe06ba603e2d64cf38ffd3723d40ace907..cb11727548bc0fa5f6e269e5bf2d650611855331 100644 (file)
@@ -32,6 +32,7 @@
 #include <proto/fd.h>
 #include <proto/log.h>
 #include <proto/hdr_idx.h>
+#include <proto/proto_tcp.h>
 #include <proto/proto_http.h>
 #include <proto/proxy.h>
 #include <proto/session.h>
@@ -179,6 +180,7 @@ int event_accept(int fd) {
                s->si[0].err_type = SI_ET_NONE;
                s->si[0].err_loc = NULL;
                s->si[0].owner = t;
+               s->si[0].connect = NULL;
                s->si[0].shutr = stream_sock_shutr;
                s->si[0].shutw = stream_sock_shutw;
                s->si[0].chk_rcv = stream_sock_chk_rcv;
@@ -191,6 +193,7 @@ int event_accept(int fd) {
                s->si[1].err_type = SI_ET_NONE;
                s->si[1].err_loc = NULL;
                s->si[1].owner = t;
+               s->si[1].connect = tcpv4_connect_server;
                s->si[1].shutr = stream_sock_shutr;
                s->si[1].shutw = stream_sock_shutw;
                s->si[1].chk_rcv = stream_sock_chk_rcv;
index 0091464dccc6257b242879f17c0abb095058e10f..6f368da98fb7f0b7a2089321eb15aaab81b930ca 100644 (file)
 #include <common/version.h>
 
 #include <types/global.h>
+#include <types/server.h>
 
 #include <proto/acl.h>
 #include <proto/backend.h>
 #include <proto/buffers.h>
 #include <proto/fd.h>
+#include <proto/log.h>
+#include <proto/port_range.h>
 #include <proto/protocols.h>
 #include <proto/proto_tcp.h>
 #include <proto/proxy.h>
@@ -173,6 +176,263 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct socka
        return 0;
 }
 
+
+/*
+ * This function initiates a connection to the server assigned to this session
+ * (s->srv, s->srv_addr). It will assign a server if none is assigned yet.
+ * It can return one of :
+ *  - SN_ERR_NONE if everything's OK
+ *  - SN_ERR_SRVTO if there are no more servers
+ *  - SN_ERR_SRVCL if the connection was refused by the server
+ *  - SN_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
+ *  - SN_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
+ *  - SN_ERR_INTERNAL for any other purely internal errors
+ * Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted.
+ */
+int tcpv4_connect_server(struct stream_interface *si,
+                        struct proxy *be, struct server *srv,
+                        struct sockaddr *srv_addr, struct sockaddr *cli_addr)
+{
+       int fd;
+
+       if ((fd = si->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+               qfprintf(stderr, "Cannot get a server socket.\n");
+
+               if (errno == ENFILE)
+                       send_log(be, LOG_EMERG,
+                                "Proxy %s reached system FD limit at %d. Please check system tunables.\n",
+                                be->id, maxfd);
+               else if (errno == EMFILE)
+                       send_log(be, LOG_EMERG,
+                                "Proxy %s reached process FD limit at %d. Please check 'ulimit-n' and restart.\n",
+                                be->id, maxfd);
+               else if (errno == ENOBUFS || errno == ENOMEM)
+                       send_log(be, LOG_EMERG,
+                                "Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n",
+                                be->id, maxfd);
+               /* this is a resource error */
+               return SN_ERR_RESOURCE;
+       }
+
+       if (fd >= global.maxsock) {
+               /* do not log anything there, it's a normal condition when this option
+                * is used to serialize connections to a server !
+                */
+               Alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
+               close(fd);
+               return SN_ERR_PRXCOND; /* it is a configuration limit */
+       }
+
+       if ((fcntl(fd, F_SETFL, O_NONBLOCK)==-1) ||
+           (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) == -1)) {
+               qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
+               close(fd);
+               return SN_ERR_INTERNAL;
+       }
+
+       if (be->options & PR_O_TCP_SRV_KA)
+               setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
+
+       if (be->options & PR_O_TCP_NOLING)
+               setsockopt(fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger));
+
+       /* allow specific binding :
+        * - server-specific at first
+        * - proxy-specific next
+        */
+       if (srv != NULL && srv->state & SRV_BIND_SRC) {
+               struct sockaddr_in *remote = NULL;
+               int ret, flags = 0;
+
+#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
+               switch (srv->state & SRV_TPROXY_MASK) {
+               case SRV_TPROXY_ADDR:
+                       remote = (struct sockaddr_in *)&srv->tproxy_addr;
+                       flags  = 3;
+                       break;
+               case SRV_TPROXY_CLI:
+                       if (cli_addr)
+                               flags |= 2;
+                       /* fall through */
+               case SRV_TPROXY_CIP:
+                       /* FIXME: what can we do if the client connects in IPv6 ? */
+                       if (cli_addr)
+                               flags |= 1;
+                       remote = (struct sockaddr_in *)cli_addr;
+                       break;
+               }
+#endif
+#ifdef SO_BINDTODEVICE
+               /* Note: this might fail if not CAP_NET_RAW */
+               if (srv->iface_name)
+                       setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, srv->iface_name, srv->iface_len + 1);
+#endif
+
+               if (srv->sport_range) {
+                       int attempts = 10; /* should be more than enough to find a spare port */
+                       struct sockaddr_in src;
+
+                       ret = 1;
+                       src = srv->source_addr;
+
+                       do {
+                               /* note: in case of retry, we may have to release a previously
+                                * allocated port, hence this loop's construct.
+                                */
+                               port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port);
+                               fdtab[fd].port_range = NULL;
+
+                               if (!attempts)
+                                       break;
+                               attempts--;
+
+                               fdtab[fd].local_port = port_range_alloc_port(srv->sport_range);
+                               if (!fdtab[fd].local_port)
+                                       break;
+
+                               fdtab[fd].port_range = srv->sport_range;
+                               src.sin_port = htons(fdtab[fd].local_port);
+
+                               ret = tcpv4_bind_socket(fd, flags, &src, remote);
+                       } while (ret != 0); /* binding NOK */
+               }
+               else {
+                       ret = tcpv4_bind_socket(fd, flags, &srv->source_addr, remote);
+               }
+
+               if (ret) {
+                       port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port);
+                       fdtab[fd].port_range = NULL;
+                       close(fd);
+
+                       if (ret == 1) {
+                               Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n",
+                                     be->id, srv->id);
+                               send_log(be, LOG_EMERG,
+                                        "Cannot bind to source address before connect() for server %s/%s.\n",
+                                        be->id, srv->id);
+                       } else {
+                               Alert("Cannot bind to tproxy source address before connect() for server %s/%s. Aborting.\n",
+                                     be->id, srv->id);
+                               send_log(be, LOG_EMERG,
+                                        "Cannot bind to tproxy source address before connect() for server %s/%s.\n",
+                                        be->id, srv->id);
+                       }
+                       return SN_ERR_RESOURCE;
+               }
+       }
+       else if (be->options & PR_O_BIND_SRC) {
+               struct sockaddr_in *remote = NULL;
+               int ret, flags = 0;
+
+#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
+               switch (be->options & PR_O_TPXY_MASK) {
+               case PR_O_TPXY_ADDR:
+                       remote = (struct sockaddr_in *)&be->tproxy_addr;
+                       flags  = 3;
+                       break;
+               case PR_O_TPXY_CLI:
+                       if (cli_addr)
+                               flags |= 2;
+                       /* fall through */
+               case PR_O_TPXY_CIP:
+                       /* FIXME: what can we do if the client connects in IPv6 ? */
+                       if (cli_addr)
+                               flags |= 1;
+                       remote = (struct sockaddr_in *)cli_addr;
+                       break;
+               }
+#endif
+#ifdef SO_BINDTODEVICE
+               /* Note: this might fail if not CAP_NET_RAW */
+               if (be->iface_name)
+                       setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, be->iface_name, be->iface_len + 1);
+#endif
+               ret = tcpv4_bind_socket(fd, flags, &be->source_addr, remote);
+               if (ret) {
+                       close(fd);
+                       if (ret == 1) {
+                               Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n",
+                                     be->id);
+                               send_log(be, LOG_EMERG,
+                                        "Cannot bind to source address before connect() for proxy %s.\n",
+                                        be->id);
+                       } else {
+                               Alert("Cannot bind to tproxy source address before connect() for proxy %s. Aborting.\n",
+                                     be->id);
+                               send_log(be, LOG_EMERG,
+                                        "Cannot bind to tproxy source address before connect() for proxy %s.\n",
+                                        be->id);
+                       }
+                       return SN_ERR_RESOURCE;
+               }
+       }
+
+#if defined(TCP_QUICKACK) && defined(SOL_TCP)
+       /* disabling tcp quick ack now allows the first request to leave the
+        * machine with the first ACK. We only do this if there are pending
+        * data in the buffer.
+        */
+       if ((be->options2 & PR_O2_SMARTCON) && si->ob->send_max)
+                setsockopt(fd, SOL_TCP, TCP_QUICKACK, (char *) &zero, sizeof(zero));
+#endif
+
+       if ((connect(fd, (struct sockaddr *)srv_addr, sizeof(struct sockaddr_in)) == -1) &&
+           (errno != EINPROGRESS) && (errno != EALREADY) && (errno != EISCONN)) {
+
+               if (errno == EAGAIN || errno == EADDRINUSE) {
+                       char *msg;
+                       if (errno == EAGAIN) /* no free ports left, try again later */
+                               msg = "no free ports";
+                       else
+                               msg = "local address already in use";
+
+                       qfprintf(stderr,"Cannot connect: %s.\n",msg);
+                       port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port);
+                       fdtab[fd].port_range = NULL;
+                       close(fd);
+                       send_log(be, LOG_EMERG,
+                                "Connect() failed for server %s/%s: %s.\n",
+                                be->id, srv->id, msg);
+                       return SN_ERR_RESOURCE;
+               } else if (errno == ETIMEDOUT) {
+                       //qfprintf(stderr,"Connect(): ETIMEDOUT");
+                       port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port);
+                       fdtab[fd].port_range = NULL;
+                       close(fd);
+                       return SN_ERR_SRVTO;
+               } else {
+                       // (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM)
+                       //qfprintf(stderr,"Connect(): %d", errno);
+                       port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port);
+                       fdtab[fd].port_range = NULL;
+                       close(fd);
+                       return SN_ERR_SRVCL;
+               }
+       }
+
+       fdtab[fd].owner = si;
+       fdtab[fd].state = FD_STCONN; /* connection in progress */
+       fdtab[fd].flags = FD_FL_TCP | FD_FL_TCP_NODELAY;
+       fdtab[fd].cb[DIR_RD].f = &stream_sock_read;
+       fdtab[fd].cb[DIR_RD].b = si->ib;
+       fdtab[fd].cb[DIR_WR].f = &stream_sock_write;
+       fdtab[fd].cb[DIR_WR].b = si->ob;
+
+       fdtab[fd].peeraddr = (struct sockaddr *)srv_addr;
+       fdtab[fd].peerlen = sizeof(struct sockaddr_in);
+
+       fd_insert(fd);
+       EV_FD_SET(fd, DIR_WR);  /* for connect status */
+
+       si->state = SI_ST_CON;
+       si->flags |= SI_FL_CAP_SPLTCP; /* TCP supports splicing */
+       si->exp = tick_add_ifset(now_ms, be->timeout.connect);
+
+       return SN_ERR_NONE;  /* connection is OK */
+}
+
+
 /* This function tries to bind a TCPv4/v6 listener. It may return a warning or
  * an error message in <err> if the message is at most <errlen> bytes long
  * (including '\0'). The return value is composed from ERR_ABORT, ERR_WARN,
index a053992277104da41e11f8bca01936463dd33aa5..8c1f2c56ac48cb7029afec505af938ea6474e904 100644 (file)
@@ -460,6 +460,7 @@ int uxst_event_accept(int fd) {
                s->si[0].err_type = SI_ET_NONE;
                s->si[0].err_loc = NULL;
                s->si[0].owner = t;
+               s->si[0].connect = NULL;
                s->si[0].shutr = stream_sock_shutr;
                s->si[0].shutw = stream_sock_shutw;
                s->si[0].chk_rcv = stream_sock_chk_rcv;
@@ -472,6 +473,7 @@ int uxst_event_accept(int fd) {
                s->si[1].err_type = SI_ET_NONE;
                s->si[1].err_loc = NULL;
                s->si[1].owner = t;
+               s->si[1].connect = NULL;
                s->si[1].shutr = stream_sock_shutr;
                s->si[1].shutw = stream_sock_shutw;
                s->si[1].chk_rcv = stream_sock_chk_rcv;