diff --git a/vlib/picoev/picoev.v b/vlib/picoev/picoev.v index eb15913a4993b4..99bbd4049f5cd7 100644 --- a/vlib/picoev/picoev.v +++ b/vlib/picoev/picoev.v @@ -1,6 +1,7 @@ module picoev import net +import os import pico_http_parser import time @@ -307,6 +308,15 @@ fn default_error_callback(_data voidptr, _req pico_http_parser.Request, mut res // new creates a `Picoev` struct and initializes the main loop. pub fn new(config Config) !&Picoev { + // A server must not die on a peer RST: any write to a connection the client + // already reset delivers SIGPIPE, whose default action terminates the process + // SILENTLY (no panic, no crash report). Long-held fds (SSE) make this routine + // rather than rare — a subscriber that disconnects between pushes turns the + // next push into a process kill. Ignore it process-wide once a server event + // loop exists; write()/send() then report EPIPE and the caller drops the fd. + $if !windows { + os.signal_ignore(.pipe) + } listening_socket_fd := listen(config) or { elog('Error during listen: ${err}') return err diff --git a/vlib/picoev/socket_util.c.v b/vlib/picoev/socket_util.c.v index 27f85d7b680124..e32d729bfe64bd 100644 --- a/vlib/picoev/socket_util.c.v +++ b/vlib/picoev/socket_util.c.v @@ -44,6 +44,16 @@ fn setup_sock(fd int) ! { if C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_NODELAY, &flag, sizeof(int)) < 0 { return error('setup_sock.setup_sock failed') } + $if freebsd || macos { + // Accepted sockets do not inherit SO_NOSIGPIPE from the listener on the + // BSDs; without it a write() after a peer RST raises SIGPIPE instead of + // returning EPIPE. Belt to the process-wide signal_ignore braces in new() + // — either alone suffices, together they survive callers that re-arm + // SIGPIPE handlers. + if C.setsockopt(fd, C.SOL_SOCKET, C.SO_NOSIGPIPE, &flag, sizeof(int)) < 0 { + return error('setup_sock: SO_NOSIGPIPE failed') + } + } $if freebsd { if C.fcntl(fd, C.F_SETFL, C.SOCK_NONBLOCK) != 0 { return error('fcntl failed')