]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: protocol: add a ->drain() function at the connection control layer
authorWilly Tarreau <w@1wt.eu>
Fri, 11 Dec 2020 15:19:12 +0000 (16:19 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 11 Dec 2020 15:26:00 +0000 (16:26 +0100)
This is what we need to drain pending incoming data from an connection.
The code was taken from conn_sock_drain() without the connection-specific
stuff. It still takes a connection for now for API simplicity.

include/haproxy/protocol-t.h
include/haproxy/sock.h
src/proto_sockpair.c
src/proto_tcp.c
src/proto_uxst.c
src/sock.c

index 5205de690159ff36f7feb265c59ea69b3efecee9..cd642b46e8c504f8d578c2a70398624f3cd0ba48 100644 (file)
@@ -99,6 +99,7 @@ struct protocol {
        void (*ctrl_init)(struct connection *);         /* completes initialization of the connection */
        void (*ctrl_close)(struct connection *);        /* completes release of the connection */
        int (*connect)(struct connection *, int flags); /* connect function if any, see below for flags values */
+       int (*drain)(struct connection *);              /* drain pending data; 0=failed, >0=success */
 
        /* functions acting on the receiver */
        int (*rx_suspend)(struct receiver *rx);         /* temporarily suspend this receiver for a soft restart */
index 8f6a90279ccb98d58cdc82914a6ddc7084285bff..20f4dee082aa8790b2e4157f4045122273fec98a 100644 (file)
@@ -47,6 +47,7 @@ void sock_conn_ctrl_init(struct connection *conn);
 void sock_conn_ctrl_close(struct connection *conn);
 void sock_conn_iocb(int fd);
 int sock_conn_check(struct connection *conn);
+int sock_drain(struct connection *conn);
 
 
 #endif /* _HAPROXY_SOCK_H */
index 7124f565f57a434b62317f673c8ce4b8ff2a03f6..9c16b2bcfe05d43bb59d390e8f93c1e05c59efcd 100644 (file)
@@ -77,6 +77,7 @@ struct protocol proto_sockpair = {
        .ctrl_init      = sock_conn_ctrl_init,
        .ctrl_close     = sock_conn_ctrl_close,
        .connect        = sockpair_connect_server,
+       .drain          = sock_drain,
 
        /* binding layer */
        /* Note: suspend/resume not supported */
index 4a5e9c21640b57aa58eddc5bbd960811de8f1a8b..dcc524ddbe82da98a58a8f5c9864a4c5d7f15c3e 100644 (file)
@@ -67,6 +67,7 @@ struct protocol proto_tcpv4 = {
        .ctrl_init      = sock_conn_ctrl_init,
        .ctrl_close     = sock_conn_ctrl_close,
        .connect        = tcp_connect_server,
+       .drain          = sock_drain,
 
        /* binding layer */
        .rx_suspend     = tcp_suspend_receiver,
@@ -106,6 +107,7 @@ struct protocol proto_tcpv6 = {
        .ctrl_init      = sock_conn_ctrl_init,
        .ctrl_close     = sock_conn_ctrl_close,
        .connect        = tcp_connect_server,
+       .drain          = sock_drain,
 
        /* binding layer */
        .rx_suspend     = tcp_suspend_receiver,
index 1c6ea77dcd3fd41863a056438ab8e9abdfa72e4f..9b1fc38d5dfc33bd79b75e237f709828c4fd3ac3 100644 (file)
@@ -63,6 +63,7 @@ struct protocol proto_uxst = {
        .ctrl_init      = sock_conn_ctrl_init,
        .ctrl_close     = sock_conn_ctrl_close,
        .connect        = uxst_connect_server,
+       .drain          = sock_drain,
 
        /* binding layer */
        .rx_suspend     = uxst_suspend_receiver,
index 5a072f0b8b0ebabf3a6806db80752f70b92767e6..cd1e3ffa63851c0f1e94c0096c54db6f4c33c669 100644 (file)
@@ -819,6 +819,59 @@ void sock_conn_iocb(int fd)
        }
 }
 
+/* Drains possibly pending incoming data on the file descriptor attached to the
+ * connection. This is used to know whether we need to disable lingering on
+ * close. Returns non-zero if it is safe to close without disabling lingering,
+ * otherwise zero.
+ */
+int sock_drain(struct connection *conn)
+{
+       int turns = 2;
+       int fd = conn->handle.fd;
+       int len;
+
+       if (fdtab[fd].ev & (FD_POLL_ERR|FD_POLL_HUP))
+               goto shut;
+
+       if (!fd_recv_ready(fd))
+               return 0;
+
+       /* no drain function defined, use the generic one */
+
+       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)
+                       goto shut;
+
+               if (len < 0) {
+                       if (errno == EAGAIN) {
+                               /* connection not closed yet */
+                               fd_cant_recv(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[fd].linger_risk = 0;
+       return 1;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8