]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: connection: move tcp_connect_probe() to conn_fd_check()
authorWilly Tarreau <w@1wt.eu>
Fri, 27 Dec 2019 09:40:21 +0000 (10:40 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 27 Dec 2019 15:38:43 +0000 (16:38 +0100)
The function is not TCP-specific at all, it covers all FD-based sockets
so let's move this where other similar functions are, in connection.c,
and rename it conn_fd_check().

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

index a7647bd8fe5a2716c9a2205a0b09883beb8df12d..001276de4efce5ba6e740e8c2bc6fa100f2d22a3 100644 (file)
@@ -43,6 +43,7 @@ extern struct mux_proto_list mux_proto_list;
  * provided by the connection's sock_ops.
  */
 void conn_fd_handler(int fd);
+int conn_fd_check(struct connection *conn);
 
 /* receive a PROXY protocol header over a connection */
 int conn_recv_proxy(struct connection *conn, int flag);
index 76f89b1f34d7837e7c89addf39c471e4558880f8..daac1d836a9f70f16d9acf0f8cb5ff5f7a89b6bf 100644 (file)
@@ -30,7 +30,6 @@
 int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct sockaddr_storage *remote);
 int tcp_pause_listener(struct listener *l);
 int tcp_connect_server(struct connection *conn, int flags);
-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);
 
index 6c8855fd532856e104ede9a39c24e74737d2ad09..3f3e99d1234d6cdab3d1e85a5fa2dbe468f2966a 100644 (file)
@@ -91,7 +91,7 @@ void conn_fd_handler(int fd)
                 * attempted yet to probe the connection. Then let's retry the
                 * connect().
                 */
-               if (!tcp_connect_probe(conn))
+               if (!conn_fd_check(conn))
                        goto leave;
        }
 
@@ -191,6 +191,97 @@ void conn_update_xprt_polling(struct connection *c)
        c->flags = f;
 }
 
+/* This is the callback which is set when a connection establishment is pending
+ * and we have nothing to send. It may update the FD polling status to indicate
+ * !READY. It returns 0 if it fails in a fatal way or needs to poll to go
+ * further, otherwise it returns non-zero and removes the CO_FL_WAIT_L4_CONN
+ * flag from the connection's flags. In case of error, it sets CO_FL_ERROR and
+ * leaves the error code in errno.
+ */
+int conn_fd_check(struct connection *conn)
+{
+       struct sockaddr_storage *addr;
+       int fd = conn->handle.fd;
+
+       if (conn->flags & CO_FL_ERROR)
+               return 0;
+
+       if (!conn_ctrl_ready(conn))
+               return 0;
+
+       if (!(conn->flags & CO_FL_WAIT_L4_CONN))
+               return 1; /* strange we were called while ready */
+
+       if (!fd_send_ready(fd))
+               return 0;
+
+       /* Here we have 2 cases :
+        *  - modern pollers, able to report ERR/HUP. If these ones return any
+        *    of these flags then it's likely a failure, otherwise it possibly
+        *    is a success (i.e. there may have been data received just before
+        *    the error was reported).
+        *  - select, which doesn't report these and with which it's always
+        *    necessary either to try connect() again or to check for SO_ERROR.
+        * In order to simplify everything, we double-check using connect() as
+        * soon as we meet either of these delicate situations. Note that
+        * SO_ERROR would clear the error after reporting it!
+        */
+       if (cur_poller.flags & HAP_POLL_F_ERRHUP) {
+               /* modern poller, able to report ERR/HUP */
+               if ((fdtab[fd].ev & (FD_POLL_IN|FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_IN)
+                       goto done;
+               if ((fdtab[fd].ev & (FD_POLL_OUT|FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_OUT)
+                       goto done;
+               if (!(fdtab[fd].ev & (FD_POLL_ERR|FD_POLL_HUP)))
+                       goto wait;
+               /* error present, fall through common error check path */
+       }
+
+       /* Use connect() to check the state of the socket. This has the double
+        * advantage of *not* clearing the error (so that health checks can
+        * still use getsockopt(SO_ERROR)) and giving us the following info :
+        *  - error
+        *  - connecting (EALREADY, EINPROGRESS)
+        *  - connected (EISCONN, 0)
+        */
+       addr = conn->dst;
+       if ((conn->flags & CO_FL_SOCKS4) && obj_type(conn->target) == OBJ_TYPE_SERVER)
+               addr = &objt_server(conn->target)->socks4_addr;
+
+       if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
+               if (errno == EALREADY || errno == EINPROGRESS)
+                       goto wait;
+
+               if (errno && errno != EISCONN)
+                       goto out_error;
+       }
+
+ done:
+       /* The FD is ready now, we'll mark the connection as complete and
+        * forward the event to the transport layer which will notify the
+        * data layer.
+        */
+       conn->flags &= ~CO_FL_WAIT_L4_CONN;
+       fd_may_send(fd);
+       fd_cond_recv(fd);
+       errno = 0; // make health checks happy
+       return 1;
+
+ out_error:
+       /* Write error on the file descriptor. Report it to the connection
+        * and disable polling on this FD.
+        */
+       fdtab[fd].linger_risk = 0;
+       conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
+       __conn_xprt_stop_both(conn);
+       return 0;
+
+ wait:
+       __conn_xprt_want_send(conn);
+       fd_cant_send(fd);
+       return 0;
+}
+
 /* Send a message over an established connection. It makes use of send() and
  * returns the same return code and errno. If the socket layer is not ready yet
  * then -1 is returned and ENOTSOCK is set into errno. If the fd is not marked
index 5ed15a985d02fa066bf89487ca67d0d31888f160..df4e5a4d2d2d8f41125eb6702af4818bd8a2f897 100644 (file)
@@ -636,97 +636,6 @@ int tcp_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir)
        }
 }
 
-/* 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
- * returns non-zero and removes the CO_FL_WAIT_L4_CONN flag from the connection's
- * flags. In case of error, it sets CO_FL_ERROR and leaves the error code in
- * errno.
- */
-int tcp_connect_probe(struct connection *conn)
-{
-       struct sockaddr_storage *addr;
-       int fd = conn->handle.fd;
-
-       if (conn->flags & CO_FL_ERROR)
-               return 0;
-
-       if (!conn_ctrl_ready(conn))
-               return 0;
-
-       if (!(conn->flags & CO_FL_WAIT_L4_CONN))
-               return 1; /* strange we were called while ready */
-
-       if (!fd_send_ready(fd))
-               return 0;
-
-       /* Here we have 2 cases :
-        *  - modern pollers, able to report ERR/HUP. If these ones return any
-        *    of these flags then it's likely a failure, otherwise it possibly
-        *    is a success (i.e. there may have been data received just before
-        *    the error was reported).
-        *  - select, which doesn't report these and with which it's always
-        *    necessary either to try connect() again or to check for SO_ERROR.
-        * In order to simplify everything, we double-check using connect() as
-        * soon as we meet either of these delicate situations. Note that
-        * SO_ERROR would clear the error after reporting it!
-        */
-       if (cur_poller.flags & HAP_POLL_F_ERRHUP) {
-               /* modern poller, able to report ERR/HUP */
-               if ((fdtab[fd].ev & (FD_POLL_IN|FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_IN)
-                       goto done;
-               if ((fdtab[fd].ev & (FD_POLL_OUT|FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_OUT)
-                       goto done;
-               if (!(fdtab[fd].ev & (FD_POLL_ERR|FD_POLL_HUP)))
-                       goto wait;
-               /* error present, fall through common error check path */
-       }
-
-       /* Use connect() to check the state of the socket. This has the double
-        * advantage of *not* clearing the error (so that health checks can
-        * still use getsockopt(SO_ERROR)) and giving us the following info :
-        *  - error
-        *  - connecting (EALREADY, EINPROGRESS)
-        *  - connected (EISCONN, 0)
-        */
-       addr = conn->dst;
-       if ((conn->flags & CO_FL_SOCKS4) && obj_type(conn->target) == OBJ_TYPE_SERVER)
-               addr = &objt_server(conn->target)->socks4_addr;
-
-       if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
-               if (errno == EALREADY || errno == EINPROGRESS)
-                       goto wait;
-
-               if (errno && errno != EISCONN)
-                       goto out_error;
-       }
-
- done:
-       /* The FD is ready now, we'll mark the connection as complete and
-        * forward the event to the transport layer which will notify the
-        * data layer.
-        */
-       conn->flags &= ~CO_FL_WAIT_L4_CONN;
-       fd_may_send(fd);
-       fd_cond_recv(fd);
-       errno = 0; // make health checks happy
-       return 1;
-
- out_error:
-       /* Write error on the file descriptor. Report it to the connection
-        * and disable polling on this FD.
-        */
-       fdtab[fd].linger_risk = 0;
-       conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
-       __conn_xprt_stop_both(conn);
-       return 0;
-
- wait:
-       __conn_xprt_want_send(conn);
-       fd_cant_send(fd);
-       return 0;
-}
-
 /* XXX: Should probably be elsewhere */
 static int compare_sockaddr(struct sockaddr_storage *a, struct sockaddr_storage *b)
 {