An HTTP/1.1 server for zig
0

Configure Feed

Select the types of activity you want to include in your feed.

try socket-correct APIs for windows

+93 -17
+2 -2
build.zig.zon
··· 9 9 .hash = "metrics-0.0.0-W7G4eIegAQD4XxA9Co7Atbw59u_2zvxYf406AZuoAHPM", 10 10 }, 11 11 .websocket = .{ 12 - .url = "https://github.com/karlseguin/websocket.zig/archive/afee2f6de22e0b0b8b08e149ef6beff8af3d8553.tar.gz", 13 - .hash = "websocket-0.1.0-ZPISdeXMBAAxqIFvM5udnayk3mEw7U6-fcL8LQ1dBsrL", 12 + .url = "https://github.com/karlseguin/websocket.zig/archive/6d309f7a9790030f2fc070323b5defc64a15c13b.tar.gz", 13 + .hash = "websocket-0.1.0-ZPISdV_aBACU9pGuvrTQ2z8uxz_Sp8JnJgQaiGKQIx1l", 14 14 }, 15 15 // .websocket = .{ .path = "../websocket.zig" }, 16 16 },
+24 -15
src/posix.zig
··· 164 164 165 165 pub fn close(fd: fd_t) void { 166 166 if (native_os == .windows) { 167 - return windows.CloseHandle(fd); 167 + // httpz only ever passes socket handles to close; `closesocket` is the 168 + // proper API on Windows (CloseHandle skips Winsock's refcount/cleanup). 169 + windows.closesocket(fd) catch {}; 170 + return; 168 171 } 169 172 switch (posix.errno(system.close(fd))) { 170 173 .BADF => unreachable, // Always a race condition. ··· 175 178 176 179 pub fn setsockopt(fd: socket_t, level: i32, optname: u32, opt: []const u8) !void { 177 180 if (native_os == .windows) { 178 - const rc = windows.ws2_32.setsockopt(fd, level, @intCast(optname), opt.ptr, @intCast(opt.len)); 181 + // Winsock takes SO_RCVTIMEO/SO_SNDTIMEO as a DWORD of milliseconds, 182 + // not a struct timeval. Translate so callers can stay portable. 183 + var ms_buf: u32 = 0; 184 + var opt_ptr: [*]const u8 = opt.ptr; 185 + var opt_len: i32 = @intCast(opt.len); 186 + if (level == SOL.SOCKET and (optname == SO.RCVTIMEO or optname == SO.SNDTIMEO) and opt.len == @sizeOf(timeval)) { 187 + const tv: *const timeval = @ptrCast(@alignCast(opt.ptr)); 188 + const total_ms = @as(i64, tv.sec) * 1000 + @divTrunc(@as(i64, tv.usec), 1000); 189 + ms_buf = if (total_ms < 0) 0 else @intCast(@min(total_ms, std.math.maxInt(u32))); 190 + opt_ptr = @ptrCast(&ms_buf); 191 + opt_len = @sizeOf(u32); 192 + } 193 + const rc = windows.ws2_32.setsockopt(fd, level, @intCast(optname), opt_ptr, opt_len); 179 194 if (rc == windows.ws2_32.SOCKET_ERROR) { 180 195 switch (windows.ws2_32.WSAGetLastError()) { 181 196 .WSANOTINITIALISED => unreachable, ··· 528 543 pub const iovec_const = posix.iovec_const; 529 544 530 545 /// Union of read errors across platforms. Declared explicitly so callers can 531 - /// switch on `error.WouldBlock` portably — on Windows `read` goes through 532 - /// `ReadFile`, which never reports it, but non-blocking sockets in general 533 - /// might, and keeping it in the type lets the rest of the codebase stay 534 - /// platform-agnostic. 546 + /// switch on `error.WouldBlock` portably regardless of OS. 535 547 pub const ReadError = error{ 536 548 WouldBlock, 537 549 ProcessNotFound, ··· 543 555 SocketNotConnected, 544 556 ConnectionResetByPeer, 545 557 ConnectionTimedOut, 546 - BrokenPipe, 547 - OperationAborted, 548 - LockViolation, 549 - AccessDenied, 558 + NetworkSubsystemFailed, 550 559 Unexpected, 551 560 }; 552 561 ··· 567 576 NoDevice, 568 577 MessageTooBig, 569 578 SystemResources, 570 - OperationAborted, 571 - LockViolation, 579 + SocketNotConnected, 580 + NetworkSubsystemFailed, 572 581 Unexpected, 573 582 }; 574 583 575 584 pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { 576 585 if (native_os == .windows) { 577 - // TODO improve this to use WriteFileScatter 586 + // TODO improve this to use WSASend with the full iovec 578 587 if (iov.len == 0) return 0; 579 588 const first = iov[0]; 580 589 return write(fd, first.base[0..first.len]); ··· 607 616 pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { 608 617 if (bytes.len == 0) return 0; 609 618 if (native_os == .windows) { 610 - return windows.WriteFile(fd, bytes, null); 619 + return windows.send(fd, bytes); 611 620 } 612 621 613 622 const max_count = switch (native_os) { ··· 645 654 pub fn read(fd: fd_t, buf: []u8) ReadError!usize { 646 655 if (buf.len == 0) return 0; 647 656 if (native_os == .windows) { 648 - return windows.ReadFile(fd, buf, null); 657 + return windows.recv(fd, buf); 649 658 } 650 659 651 660 // Prevents EINVAL.
+67
src/windows.zig
··· 105 105 } 106 106 } 107 107 108 + pub const RecvError = error{ 109 + WouldBlock, 110 + ConnectionResetByPeer, 111 + SocketNotConnected, 112 + NetworkSubsystemFailed, 113 + Unexpected, 114 + }; 115 + 116 + /// Synchronous `recv` for a `WSA_FLAG_OVERLAPPED` socket. Used in place of 117 + /// `ReadFile`, which fails with INVALID_PARAMETER on overlapped sockets when 118 + /// passed a null OVERLAPPED. Returns 0 on graceful shutdown. 119 + pub fn recv(s: ws2_32.SOCKET, buf: []u8) RecvError!usize { 120 + const len: i32 = @intCast(@min(buf.len, std.math.maxInt(i32))); 121 + const rc = ws2_32.recv(s, buf.ptr, len, 0); 122 + if (rc != ws2_32.SOCKET_ERROR) return @intCast(rc); 123 + switch (ws2_32.WSAGetLastError()) { 124 + .WSAEWOULDBLOCK => return error.WouldBlock, 125 + .WSAECONNRESET, 126 + .WSAECONNABORTED, 127 + .WSAENETRESET, 128 + .WSAETIMEDOUT, 129 + => return error.ConnectionResetByPeer, 130 + .WSAENETDOWN => return error.NetworkSubsystemFailed, 131 + .WSAENOTCONN => return error.SocketNotConnected, 132 + .WSAESHUTDOWN => return 0, 133 + .WSAEINTR, .WSAEINPROGRESS => unreachable, 134 + .WSAEINVAL, .WSAEFAULT => unreachable, 135 + .WSAENOTSOCK => unreachable, 136 + .WSAEOPNOTSUPP => unreachable, 137 + else => |err| return unexpectedWSAError(err), 138 + } 139 + } 140 + 141 + pub const SendError = error{ 142 + WouldBlock, 143 + ConnectionResetByPeer, 144 + BrokenPipe, 145 + SocketNotConnected, 146 + NetworkSubsystemFailed, 147 + AccessDenied, 148 + SystemResources, 149 + MessageTooBig, 150 + Unexpected, 151 + }; 152 + 153 + /// Synchronous `send` for a `WSA_FLAG_OVERLAPPED` socket. 154 + pub fn send(s: ws2_32.SOCKET, bytes: []const u8) SendError!usize { 155 + const len: i32 = @intCast(@min(bytes.len, std.math.maxInt(i32))); 156 + const rc = ws2_32.send(s, bytes.ptr, len, 0); 157 + if (rc != ws2_32.SOCKET_ERROR) return @intCast(rc); 158 + switch (ws2_32.WSAGetLastError()) { 159 + .WSAEWOULDBLOCK => return error.WouldBlock, 160 + .WSAECONNRESET, .WSAENETRESET, .WSAETIMEDOUT, .WSAEHOSTUNREACH => return error.ConnectionResetByPeer, 161 + .WSAECONNABORTED, .WSAESHUTDOWN => return error.BrokenPipe, 162 + .WSAENETDOWN => return error.NetworkSubsystemFailed, 163 + .WSAENOTCONN => return error.SocketNotConnected, 164 + .WSAEACCES => return error.AccessDenied, 165 + .WSAENOBUFS => return error.SystemResources, 166 + .WSAEMSGSIZE => return error.MessageTooBig, 167 + .WSAEINTR, .WSAEINPROGRESS => unreachable, 168 + .WSAEINVAL, .WSAEFAULT => unreachable, 169 + .WSAENOTSOCK => unreachable, 170 + .WSAEOPNOTSUPP => unreachable, 171 + else => |err| return unexpectedWSAError(err), 172 + } 173 + } 174 + 108 175 pub const ReadFileError = error{ 109 176 BrokenPipe, 110 177 /// The specified network name is no longer available.