MINOR: connection: implement conn_sock_send()
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.
diff --git a/include/proto/connection.h b/include/proto/connection.h
index 5f80cc5..27922f3 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -45,6 +45,9 @@
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)
{
diff --git a/src/connection.c b/src/connection.c
index c21c98b..8b3c835 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -218,6 +218,56 @@
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
*/