Skip to content

Commit

Permalink
Prevent sendmsg/write raising SIGPIPE the TCP socket is closed:
Browse files Browse the repository at this point in the history
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
  • Loading branch information
zsx committed May 26, 2017
1 parent 2dba411 commit 9f38464
Showing 1 changed file with 25 additions and 5 deletions.
30 changes: 25 additions & 5 deletions src/os/dev-net.c
Expand Up @@ -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

/***********************************************************************
**
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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));
Expand Down

0 comments on commit 9f38464

Please sign in to comment.