]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: connection: implement conn_sock_send()
authorWilly Tarreau <w@1wt.eu>
Thu, 12 Mar 2015 22:56:52 +0000 (23:56 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 12 Mar 2015 23:04:49 +0000 (00:04 +0100)
This function is an equivalent to send() which operates over a connection
instead of a file descriptor. It checks that the control layer is ready
and that it's allowed to send. If automatically enables polling if it
cannot send. It simplifies the return checks by returning zero in all
cases where it cannot send so that the caller only has to care about
negative values indicating errors.

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

index 5f80cc5ba4ca8dd4ce6e41a48db5e518beebd740..27922f3dbf86c04b7478e94d20034dec02065eec 100644 (file)
@@ -45,6 +45,9 @@ int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connectio
 int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst);
 int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote);
 
+/* raw send() directly on the socket */
+int conn_sock_send(struct connection *conn, const void *buf, int len, int flags);
+
 /* returns true is the transport layer is ready */
 static inline int conn_xprt_ready(const struct connection *conn)
 {
index c21c98bae95a6ffd12e6f41f84eb9311d0ec4b9b..8b3c835e767de5843f4ba6e0a29de7715d184308 100644 (file)
@@ -218,6 +218,56 @@ void conn_update_sock_polling(struct connection *c)
        c->flags = f;
 }
 
+/* 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
+ * as ready, or if EAGAIN or ENOTCONN is returned, then we return 0. It returns
+ * EMSGSIZE if called with a zero length message. The purpose is to simplify
+ * some rare attempts to directly write on the socket from above the connection
+ * (typically send_proxy). In case of EAGAIN, the fd is marked as "cant_send".
+ * It automatically retries on EINTR. Other errors cause the connection to be
+ * marked as in error state. It takes similar arguments as send() except the
+ * first one which is the connection instead of the file descriptor. Note,
+ * MSG_DONTWAIT and MSG_NOSIGNAL are forced on the flags.
+ */
+int conn_sock_send(struct connection *conn, const void *buf, int len, int flags)
+{
+       int ret;
+
+       ret = -1;
+       errno = ENOTSOCK;
+
+       if (conn->flags & CO_FL_SOCK_WR_SH)
+               goto fail;
+
+       if (!conn_ctrl_ready(conn))
+               goto fail;
+
+       errno = EMSGSIZE;
+       if (!len)
+               goto fail;
+
+       if (!fd_send_ready(conn->t.sock.fd))
+               goto wait;
+
+       do {
+               ret = send(conn->t.sock.fd, buf, len, flags | MSG_DONTWAIT | MSG_NOSIGNAL);
+       } while (ret < 0 && errno == EINTR);
+
+
+       if (ret > 0)
+               return ret;
+
+       if (ret == 0 || errno == EAGAIN || errno == ENOTCONN) {
+       wait:
+               fd_cant_send(conn->t.sock.fd);
+               return 0;
+       }
+ fail:
+       conn->flags |= CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH | CO_FL_ERROR;
+       return ret;
+}
+
 /*
  * Get data length from tlv
  */