]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: connection: make conn_sock_drain() work for all socket families
authorWilly Tarreau <w@1wt.eu>
Fri, 24 Aug 2018 12:31:53 +0000 (14:31 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 24 Aug 2018 12:45:46 +0000 (14:45 +0200)
This patch improves the previous fix by implementing the socket draining
code directly in conn_sock_drain() so that it always applies regardless
of the protocol's family. Thus it gets rid of tcp_drain().

include/proto/proto_tcp.h
src/connection.c
src/proto_tcp.c
src/proto_uxst.c

index 5241e7cafb8f571171cd7d9a4e765ae4ab560a82..d4e09c7a65adee0f24ba4cf2ab2c5c706c5bc06f 100644 (file)
@@ -33,7 +33,6 @@ int tcp_connect_server(struct connection *conn, int data, int delack);
 int tcp_connect_probe(struct connection *conn);
 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_drain(int fd);
 
 /* Export some samples. */
 int smp_fetch_src(const struct arg *args, struct sample *smp, const char *kw, void *private);
index b3ef56eba650807dafcd185d1361cd64360d9aee..ab32567bd566c2a5a44e66f7284753cd814846d6 100644 (file)
@@ -359,29 +359,61 @@ int conn_subscribe(struct connection *conn, int event_type, void *param)
  */
 int conn_sock_drain(struct connection *conn)
 {
+       int turns = 2;
+       int len;
+
        if (!conn_ctrl_ready(conn))
                return 1;
 
        if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH))
                return 1;
 
-       if (fdtab[conn->handle.fd].ev & (FD_POLL_ERR|FD_POLL_HUP)) {
-               fdtab[conn->handle.fd].linger_risk = 0;
-       }
-       else {
-               if (!fd_recv_ready(conn->handle.fd))
-                       return 0;
+       if (fdtab[conn->handle.fd].ev & (FD_POLL_ERR|FD_POLL_HUP))
+               goto shut;
 
-               /* disable draining if we were called and have no drain function */
-               if (!conn->ctrl->drain) {
-                       __conn_xprt_stop_recv(conn);
-                       return 0;
-               }
+       if (!fd_recv_ready(conn->handle.fd))
+               return 0;
 
+       if (conn->ctrl->drain) {
                if (conn->ctrl->drain(conn->handle.fd) <= 0)
                        return 0;
+               goto shut;
+       }
+
+       /* no drain function defined, use the generic one */
+
+       while (turns) {
+#ifdef MSG_TRUNC_CLEARS_INPUT
+               len = recv(conn->handle.fd, NULL, INT_MAX, MSG_DONTWAIT | MSG_NOSIGNAL | MSG_TRUNC);
+               if (len == -1 && errno == EFAULT)
+#endif
+                       len = recv(conn->handle.fd, trash.area, trash.size,
+                                  MSG_DONTWAIT | MSG_NOSIGNAL);
+
+               if (len == 0)
+                       goto shut;
+
+               if (len < 0) {
+                       if (errno == EAGAIN) {
+                               /* connection not closed yet */
+                               fd_cant_recv(conn->handle.fd);
+                               break;
+                       }
+                       if (errno == EINTR)  /* oops, try again */
+                               continue;
+                       /* other errors indicate a dead connection, fine. */
+                       goto shut;
+               }
+               /* OK we read some data, let's try again once */
+               turns--;
        }
 
+       /* some data are still present, give up */
+       return 0;
+
+ shut:
+       /* we're certain the connection was shut down */
+       fdtab[conn->handle.fd].linger_risk = 0;
        conn->flags |= CO_FL_SOCK_RD_SH;
        return 1;
 }
index d49ecf4c7b9a0b35ae472b1d4d98daa3ce481da9..c000602b64f624a73f421b39e9c52f0c4061c04d 100644 (file)
@@ -83,7 +83,6 @@ static struct protocol proto_tcpv4 = {
        .enable_all = enable_all_listeners,
        .get_src = tcp_get_src,
        .get_dst = tcp_get_dst,
-       .drain = tcp_drain,
        .pause = tcp_pause_listener,
        .add = tcpv4_add_listener,
        .listeners = LIST_HEAD_INIT(proto_tcpv4.listeners),
@@ -107,7 +106,6 @@ static struct protocol proto_tcpv6 = {
        .enable_all = enable_all_listeners,
        .get_src = tcp_get_src,
        .get_dst = tcp_get_dst,
-       .drain = tcp_drain,
        .pause = tcp_pause_listener,
        .add = tcpv6_add_listener,
        .listeners = LIST_HEAD_INIT(proto_tcpv6.listeners),
@@ -616,49 +614,6 @@ int tcp_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir)
        }
 }
 
-/* Tries to drain any pending incoming data from the socket to reach the
- * receive shutdown. Returns positive if the shutdown was found, negative
- * if EAGAIN was hit, otherwise zero. This is useful to decide whether we
- * can close a connection cleanly are we must kill it hard.
- */
-int tcp_drain(int fd)
-{
-       int turns = 2;
-       int len;
-
-       while (turns) {
-#ifdef MSG_TRUNC_CLEARS_INPUT
-               len = recv(fd, NULL, INT_MAX, MSG_DONTWAIT | MSG_NOSIGNAL | MSG_TRUNC);
-               if (len == -1 && errno == EFAULT)
-#endif
-                       len = recv(fd, trash.area, trash.size,
-                                  MSG_DONTWAIT | MSG_NOSIGNAL);
-
-               if (len == 0) {
-                       /* cool, shutdown received */
-                       fdtab[fd].linger_risk = 0;
-                       return 1;
-               }
-
-               if (len < 0) {
-                       if (errno == EAGAIN) {
-                               /* connection not closed yet */
-                               fd_cant_recv(fd);
-                               return -1;
-                       }
-                       if (errno == EINTR)  /* oops, try again */
-                               continue;
-                       /* other errors indicate a dead connection, fine. */
-                       fdtab[fd].linger_risk = 0;
-                       return 1;
-               }
-               /* OK we read some data, let's try again once */
-               turns--;
-       }
-       /* some data are still present, give up */
-       return 0;
-}
-
 /* This is the callback which is set when a connection establishment is pending
  * and we have nothing to send. It updates the FD polling status. It returns 0
  * if it fails in a fatal way or needs to poll to go further, otherwise it
index 9c04d6083673abdc5920fc1a2c96160c618c933f..ab788bde720fa1f1a252b787ce8387f808d2efbe 100644 (file)
@@ -42,7 +42,6 @@
 #include <proto/listener.h>
 #include <proto/log.h>
 #include <proto/protocol.h>
-#include <proto/proto_tcp.h>
 #include <proto/task.h>
 
 static int uxst_bind_listener(struct listener *listener, char *errmsg, int errlen);
@@ -72,7 +71,6 @@ static struct protocol proto_unix = {
        .disable_all = disable_all_listeners,
        .get_src = uxst_get_src,
        .get_dst = uxst_get_dst,
-       .drain = tcp_drain,
        .pause = uxst_pause_listener,
        .add = uxst_add_listener,
        .listeners = LIST_HEAD_INIT(proto_unix.listeners),