From: Willy Tarreau Date: Fri, 11 Dec 2020 15:19:12 +0000 (+0100) Subject: MINOR: protocol: add a ->drain() function at the connection control layer X-Git-Tag: v2.4-dev3~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=427c846cc97d9b3994541a36b91a91c4e57112fe;p=thirdparty%2Fhaproxy.git MINOR: protocol: add a ->drain() function at the connection control layer 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. --- diff --git a/include/haproxy/protocol-t.h b/include/haproxy/protocol-t.h index 5205de6901..cd642b46e8 100644 --- a/include/haproxy/protocol-t.h +++ b/include/haproxy/protocol-t.h @@ -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 */ diff --git a/include/haproxy/sock.h b/include/haproxy/sock.h index 8f6a90279c..20f4dee082 100644 --- a/include/haproxy/sock.h +++ b/include/haproxy/sock.h @@ -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 */ diff --git a/src/proto_sockpair.c b/src/proto_sockpair.c index 7124f565f5..9c16b2bcfe 100644 --- a/src/proto_sockpair.c +++ b/src/proto_sockpair.c @@ -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 */ diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 4a5e9c2164..dcc524ddbe 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -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, diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 1c6ea77dcd..9b1fc38d5d 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -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, diff --git a/src/sock.c b/src/sock.c index 5a072f0b8b..cd1e3ffa63 100644 --- a/src/sock.c +++ b/src/sock.c @@ -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