diff --git a/src/bsd.c b/src/bsd.c index d35eae98..fde11c20 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -418,7 +418,15 @@ int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length chunks[1].iov_base = (char *)payload; chunks[1].iov_len = payload_length; +#ifdef MSG_NOSIGNAL + /* Match bsd_send: a disconnected peer must not terminate the process. */ + struct msghdr message = {0}; + message.msg_iov = chunks; + message.msg_iovlen = 2; + return sendmsg(fd, &message, MSG_NOSIGNAL); +#else return writev(fd, chunks, 2); +#endif } #else int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length) { @@ -433,6 +441,27 @@ int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length } #endif +int bsd_write3(LIBUS_SOCKET_DESCRIPTOR fd, const char *prefix, int prefix_length, const char *data, int data_length, const char *suffix, int suffix_length) { +#ifndef _WIN32 + struct iovec chunks[3] = {{(char *) prefix, (size_t) prefix_length}, + {(char *) data, (size_t) data_length}, + {(char *) suffix, (size_t) suffix_length}}; +#ifdef MSG_NOSIGNAL + struct msghdr message = {0}; + message.msg_iov = chunks; + message.msg_iovlen = 3; + return sendmsg(fd, &message, MSG_NOSIGNAL); +#else + return writev(fd, chunks, 3); +#endif +#else + int written = bsd_send(fd, prefix, prefix_length, 1); + if (written != prefix_length) return written; + int more = bsd_write2(fd, data, data_length, suffix, suffix_length); + return written + (more < 0 ? 0 : more); +#endif +} + int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more) { // MSG_MORE (Linux), MSG_PARTIAL (Windows), TCP_NOPUSH (BSD) diff --git a/src/internal/networking/bsd.h b/src/internal/networking/bsd.h index 485784a7..8a79c448 100644 --- a/src/internal/networking/bsd.h +++ b/src/internal/networking/bsd.h @@ -91,6 +91,7 @@ LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags); int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more); int bsd_write2(LIBUS_SOCKET_DESCRIPTOR fd, const char *header, int header_length, const char *payload, int payload_length); +int bsd_write3(LIBUS_SOCKET_DESCRIPTOR fd, const char *prefix, int prefix_length, const char *data, int data_length, const char *suffix, int suffix_length); int bsd_would_block(); // return LIBUS_SOCKET_ERROR or the fd that represents listen socket diff --git a/src/libusockets.h b/src/libusockets.h index 075568f2..e0fee43f 100644 --- a/src/libusockets.h +++ b/src/libusockets.h @@ -293,6 +293,9 @@ int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length /* Special path for non-SSL sockets. Used to send header and payload in one go. Works like us_socket_write. */ int us_socket_write2(int ssl, struct us_socket_t *s, const char *header, int header_length, const char *payload, int payload_length); +/* Same non-SSL semantics as write2, with three buffers. Their combined length must fit in an int. */ +int us_socket_write3(int ssl, struct us_socket_t *s, const char *prefix, int prefix_length, const char *data, int data_length, const char *suffix, int suffix_length); + /* Set a low precision, high performance timer on a socket. A socket can only have one single active timer * at any given point in time. Will remove any such pre set timer */ void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds); diff --git a/src/socket.c b/src/socket.c index 30f30f55..e066d69e 100644 --- a/src/socket.c +++ b/src/socket.c @@ -164,12 +164,25 @@ int us_socket_write2(int ssl, struct us_socket_t *s, const char *header, int hea int written = bsd_write2(us_poll_fd(&s->p), header, header_length, payload, payload_length); if (written != header_length + payload_length) { + s->context->loop->data.last_write_failed = 1; us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); } return written < 0 ? 0 : written; } +int us_socket_write3(int ssl, struct us_socket_t *s, const char *prefix, int prefix_length, const char *data, int data_length, const char *suffix, int suffix_length) { + if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) { + return 0; + } + int written = bsd_write3(us_poll_fd(&s->p), prefix, prefix_length, data, data_length, suffix, suffix_length); + if (written != prefix_length + data_length + suffix_length) { + s->context->loop->data.last_write_failed = 1; + us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); + } + return written < 0 ? 0 : written; +} + int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) { #ifndef LIBUS_NO_SSL if (ssl) {