]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: tcp: report connection error at the connection level
authorWilly Tarreau <w@1wt.eu>
Fri, 24 Jan 2014 15:08:19 +0000 (16:08 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 24 Jan 2014 15:15:04 +0000 (16:15 +0100)
Now when a connection error happens, it is reported in the connection
so that upper layers know exactly what happened. This is particularly
useful with health checks and resources exhaustion.

src/proto_tcp.c

index d01be31bd93054d4f1fa57a9b4d56645c3351a71..ab8f343bbc78824c65cf753bcccb542712ebda11 100644 (file)
@@ -279,6 +279,8 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
        struct proxy *be;
        struct conn_src *src;
 
+       conn->flags = CO_FL_WAIT_L4_CONN; /* connection in progress */
+
        switch (obj_type(conn->target)) {
        case OBJ_TYPE_PROXY:
                be = objt_proxy(conn->target);
@@ -289,25 +291,39 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
                be = srv->proxy;
                break;
        default:
+               conn->flags |= CO_FL_ERROR;
                return SN_ERR_INTERNAL;
        }
 
        if ((fd = conn->t.sock.fd = socket(conn->addr.to.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
                qfprintf(stderr, "Cannot get a server socket.\n");
 
-               if (errno == ENFILE)
+               if (errno == ENFILE) {
+                       conn->err_code = CO_ER_SYS_FDLIM;
                        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)
+               }
+               else if (errno == EMFILE) {
+                       conn->err_code = CO_ER_PROC_FDLIM;
                        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)
+               }
+               else if (errno == ENOBUFS || errno == ENOMEM) {
+                       conn->err_code = CO_ER_SYS_MEMLIM;
                        send_log(be, LOG_EMERG,
                                 "Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n",
                                 be->id, maxfd);
+               }
+               else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
+                       conn->err_code = CO_ER_NOPROTO;
+               }
+               else
+                       conn->err_code = CO_ER_SOCK_ERR;
+
                /* this is a resource error */
+               conn->flags |= CO_FL_ERROR;
                return SN_ERR_RESOURCE;
        }
 
@@ -317,6 +333,8 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
                 */
                Alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
                close(fd);
+               conn->err_code = CO_ER_CONF_FDLIM;
+               conn->flags |= CO_FL_ERROR;
                return SN_ERR_PRXCOND; /* it is a configuration limit */
        }
 
@@ -324,6 +342,8 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
            (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) == -1)) {
                qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
                close(fd);
+               conn->err_code = CO_ER_SOCK_ERR;
+               conn->flags |= CO_FL_ERROR;
                return SN_ERR_INTERNAL;
        }
 
@@ -382,17 +402,23 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
                                attempts--;
 
                                fdinfo[fd].local_port = port_range_alloc_port(src->sport_range);
-                               if (!fdinfo[fd].local_port)
+                               if (!fdinfo[fd].local_port) {
+                                       conn->err_code = CO_ER_PORT_RANGE;
                                        break;
+                               }
 
                                fdinfo[fd].port_range = src->sport_range;
                                set_host_port(&sa, fdinfo[fd].local_port);
 
                                ret = tcp_bind_socket(fd, flags, &sa, &conn->addr.from);
+                               if (ret != 0)
+                                       conn->err_code = CO_ER_CANT_BIND;
                        } while (ret != 0); /* binding NOK */
                }
                else {
                        ret = tcp_bind_socket(fd, flags, &src->source_addr, &conn->addr.from);
+                       if (ret != 0)
+                               conn->err_code = CO_ER_CANT_BIND;
                }
 
                if (unlikely(ret != 0)) {
@@ -413,6 +439,7 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
                                         "Cannot bind to tproxy source address before connect() for backend %s.\n",
                                         be->id);
                        }
+                       conn->flags |= CO_FL_ERROR;
                        return SN_ERR_RESOURCE;
                }
        }
@@ -440,22 +467,29 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
 
                if (errno == EAGAIN || errno == EADDRINUSE || errno == EADDRNOTAVAIL) {
                        char *msg;
-                       if (errno == EAGAIN || errno == EADDRNOTAVAIL)
+                       if (errno == EAGAIN || errno == EADDRNOTAVAIL) {
                                msg = "no free ports";
-                       else
+                               conn->err_code = CO_ER_FREE_PORTS;
+                       }
+                       else {
                                msg = "local address already in use";
+                               conn->err_code = CO_ER_ADDR_INUSE;
+                       }
 
                        qfprintf(stderr,"Connect() failed for backend %s: %s.\n", be->id, msg);
                        port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
                        fdinfo[fd].port_range = NULL;
                        close(fd);
                        send_log(be, LOG_ERR, "Connect() failed for backend %s: %s.\n", be->id, msg);
+                       conn->flags |= CO_FL_ERROR;
                        return SN_ERR_RESOURCE;
                } else if (errno == ETIMEDOUT) {
                        //qfprintf(stderr,"Connect(): ETIMEDOUT");
                        port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
                        fdinfo[fd].port_range = NULL;
                        close(fd);
+                       conn->err_code = CO_ER_SOCK_ERR;
+                       conn->flags |= CO_FL_ERROR;
                        return SN_ERR_SRVTO;
                } else {
                        // (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM)
@@ -463,11 +497,12 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
                        port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
                        fdinfo[fd].port_range = NULL;
                        close(fd);
+                       conn->err_code = CO_ER_SOCK_ERR;
+                       conn->flags |= CO_FL_ERROR;
                        return SN_ERR_SRVCL;
                }
        }
 
-       conn->flags  = CO_FL_WAIT_L4_CONN; /* connection in progress */
        conn->flags |= CO_FL_ADDR_TO_SET;
 
        /* Prepare to send a few handshakes related to the on-wire protocol. */
@@ -480,6 +515,7 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
 
        if (conn_xprt_init(conn) < 0) {
                conn_force_close(conn);
+               conn->flags |= CO_FL_ERROR;
                return SN_ERR_RESOURCE;
        }