websocket forked from zzstoatzz.io/websocket.zig

client: best-effort setsockopt on connection sockets v0.1.11

std.posix.setsockopt maps BADF/NOTSOCK/INVAL/FAULT to `unreachable` ("always a race condition"). That holds for a listening socket. It does not hold for a connection socket: the peer can reset, or another thread can close the fd, between connect and the timeout call. `unreachable` cannot be caught, so the race aborts the process instead of returning an error the caller could reconnect on. Found downstream in Stream (zat.dev/stream), whose firehose consumer aborted roughly one full-suite run in seven with `reached unreachable code` in setsockopt, reached from Stream.setsockopt via readTimeout inside HandShakeReply.read during zat's connectAndRead. An upstream relay dropping the connection around handshake killed the process -- previously misread as a transient transport error. Route the client's connection-socket setsockopt through setConnSockOpt, which issues the raw syscall and swallows exactly the four arms the stdlib declares impossible while keeping every other errno meaningful. The timeouts are advisory: if the socket really is gone, the following read or write reports it properly. This is the client-side counterpart of 00bed03, which did the same for the four server connection-socket call sites. Stream.setsockopt keeps its `!void` signature so no consumer's `try` breaks. Test closes the fd under a live Stream and asserts the timeout calls return instead of aborting; before this change it panics on `.BADF => unreachable`. 64 of 64 tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>


Author zzstoatzz Co-author Claude Opus 5 (1M context) Date Commit Parent 11e0f5c4
+57 -1
1 changed file
+57 -1
1 changed file