From 9f38464c741175a58cfb8a4da540173d556147f4 Mon Sep 17 00:00:00 2001 From: Shixin Zeng Date: Fri, 26 May 2017 16:11:32 -0400 Subject: [PATCH] Prevent sendmsg/write raising SIGPIPE the TCP socket is closed: This is observed by @gchiu etc when testing the http server: the problem is that the web server writes to the client when the client has closed the connection this happens when the browser client does a refresh while not having received yet the full response so the server code keeps writing to a broken pipe which doesn't seem to matter in windows but gives this signal in osx so it's seen on multiple refreshes with a browser client on the web server we saw the problem too on android but it went away when we trapped the writes There are a few ways to prevent it crashing: 1. installing a signal handler for SIGPIPE 2. set SO_NOSIGPIPE option on the socket (only supported on some platforms) 3. set MSG_NO_SIGNAL flag on the sendto (only supported on Linux) This commit tries to do both 2 and/or 3 when supported. See: https://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly --- src/os/dev-net.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/os/dev-net.c b/src/os/dev-net.c index 82f55442ee..5906fb4b76 100644 --- a/src/os/dev-net.c +++ b/src/os/dev-net.c @@ -61,6 +61,13 @@ DEVICE_CMD Listen_Socket(REBREQ *sock); extern HWND Event_Handle; // For WSAAsync API #endif +// Prevent sendmsg/write raising SIGPIPE the TCP socket is closed: +// https://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly +// Linux does not support SO_NOSIGPIPE +// +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif /*********************************************************************** ** @@ -89,8 +96,17 @@ static void Get_Local_IP(struct devreq_net *sock) sock->local_port = ntohs(sa.sin_port); } -static REBOOL Nonblocking_Mode(SOCKET sock) +static REBOOL Set_Sock_Options(SOCKET sock) { + // Prevent sendmsg/write raising SIGPIPE the TCP socket is closed: + // https://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly +#if defined(SO_NOSIGPIPE) + int on = 1; + if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)) < 0) { + return FALSE; + } +#endif + // Set non-blocking mode. Return TRUE if no error. #ifdef FIONBIO unsigned long mode = 1; @@ -190,7 +206,7 @@ DEVICE_CMD Open_Socket(REBREQ *sock) SET_FLAG(sock->state, RSM_OPEN); // Set socket to non-blocking async mode: - if (!Nonblocking_Mode(sock->requestee.socket)) { + if (!Set_Sock_Options(sock->requestee.socket)) { sock->error = GET_ERROR; return DR_ERROR; } @@ -391,7 +407,7 @@ DEVICE_CMD Transfer_Socket(REBREQ *req) result = sendto( req->requestee.socket, s_cast(req->common.data), len, - 0, // Flags + MSG_NOSIGNAL, // Flags cast(struct sockaddr*, &remote_addr), addr_len ); WATCH2("send() len: %d actual: %d\n", len, result); @@ -620,6 +636,12 @@ DEVICE_CMD Accept_Socket(REBREQ *req) return DR_ERROR; } + if (!Set_Sock_Options(result)) { + req->error = GET_ERROR; + //Signal_Device(sock, EVT_ERROR); + return DR_ERROR; + } + // To report the new socket, the code here creates a temporary // request and copies the listen request to it. Then, it stores // the new values for IP and ports and links this request to the @@ -637,8 +659,6 @@ DEVICE_CMD Accept_Socket(REBREQ *req) news->remote_port = ntohs(sa.sin_port); Get_Local_IP(news); - Nonblocking_Mode(news->devreq.requestee.socket); - // There could be mulitple connections to be accepted. // Queue them at common.sock Attach_Request(cast(REBREQ**, &AS_REBREQ(sock)->common.sock), AS_REBREQ(news));