alpha
Login
or
Join now
karlseguin.tngl.sh
/
http.zig
Star
0
Fork
1
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
An HTTP/1.1 server for zig
Star
0
Fork
1
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
try socket-correct APIs for windows
author
Karl Seguin
date
2 months ago
(May 16, 2026, 5:02 PM +0800)
commit
c592d2c5
c592d2c5ae29bb64effdd3cadab904a52b12990e
parent
6504519e
6504519ecd32c7d329c11d58665b97a2ff809637
+93
-17
3 changed files
Expand all
Collapse all
Unified
Split
build.zig.zon
src
posix.zig
windows.zig
+2
-2
build.zig.zon
View file
Reviewed
···
9
9
.hash = "metrics-0.0.0-W7G4eIegAQD4XxA9Co7Atbw59u_2zvxYf406AZuoAHPM",
10
10
},
11
11
.websocket = .{
12
12
-
.url = "https://github.com/karlseguin/websocket.zig/archive/afee2f6de22e0b0b8b08e149ef6beff8af3d8553.tar.gz",
13
13
-
.hash = "websocket-0.1.0-ZPISdeXMBAAxqIFvM5udnayk3mEw7U6-fcL8LQ1dBsrL",
12
12
+
.url = "https://github.com/karlseguin/websocket.zig/archive/6d309f7a9790030f2fc070323b5defc64a15c13b.tar.gz",
13
13
+
.hash = "websocket-0.1.0-ZPISdV_aBACU9pGuvrTQ2z8uxz_Sp8JnJgQaiGKQIx1l",
14
14
},
15
15
// .websocket = .{ .path = "../websocket.zig" },
16
16
},
+24
-15
src/posix.zig
View file
Reviewed
···
164
164
165
165
pub fn close(fd: fd_t) void {
166
166
if (native_os == .windows) {
167
167
-
return windows.CloseHandle(fd);
167
167
+
// httpz only ever passes socket handles to close; `closesocket` is the
168
168
+
// proper API on Windows (CloseHandle skips Winsock's refcount/cleanup).
169
169
+
windows.closesocket(fd) catch {};
170
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
178
-
const rc = windows.ws2_32.setsockopt(fd, level, @intCast(optname), opt.ptr, @intCast(opt.len));
181
181
+
// Winsock takes SO_RCVTIMEO/SO_SNDTIMEO as a DWORD of milliseconds,
182
182
+
// not a struct timeval. Translate so callers can stay portable.
183
183
+
var ms_buf: u32 = 0;
184
184
+
var opt_ptr: [*]const u8 = opt.ptr;
185
185
+
var opt_len: i32 = @intCast(opt.len);
186
186
+
if (level == SOL.SOCKET and (optname == SO.RCVTIMEO or optname == SO.SNDTIMEO) and opt.len == @sizeOf(timeval)) {
187
187
+
const tv: *const timeval = @ptrCast(@alignCast(opt.ptr));
188
188
+
const total_ms = @as(i64, tv.sec) * 1000 + @divTrunc(@as(i64, tv.usec), 1000);
189
189
+
ms_buf = if (total_ms < 0) 0 else @intCast(@min(total_ms, std.math.maxInt(u32)));
190
190
+
opt_ptr = @ptrCast(&ms_buf);
191
191
+
opt_len = @sizeOf(u32);
192
192
+
}
193
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
531
-
/// switch on `error.WouldBlock` portably — on Windows `read` goes through
532
532
-
/// `ReadFile`, which never reports it, but non-blocking sockets in general
533
533
-
/// might, and keeping it in the type lets the rest of the codebase stay
534
534
-
/// platform-agnostic.
546
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
546
-
BrokenPipe,
547
547
-
OperationAborted,
548
548
-
LockViolation,
549
549
-
AccessDenied,
558
558
+
NetworkSubsystemFailed,
550
559
Unexpected,
551
560
};
552
561
···
567
576
NoDevice,
568
577
MessageTooBig,
569
578
SystemResources,
570
570
-
OperationAborted,
571
571
-
LockViolation,
579
579
+
SocketNotConnected,
580
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
577
-
// TODO improve this to use WriteFileScatter
586
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
610
-
return windows.WriteFile(fd, bytes, null);
619
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
648
-
return windows.ReadFile(fd, buf, null);
657
657
+
return windows.recv(fd, buf);
649
658
}
650
659
651
660
// Prevents EINVAL.
+67
src/windows.zig
View file
Reviewed
···
105
105
}
106
106
}
107
107
108
108
+
pub const RecvError = error{
109
109
+
WouldBlock,
110
110
+
ConnectionResetByPeer,
111
111
+
SocketNotConnected,
112
112
+
NetworkSubsystemFailed,
113
113
+
Unexpected,
114
114
+
};
115
115
+
116
116
+
/// Synchronous `recv` for a `WSA_FLAG_OVERLAPPED` socket. Used in place of
117
117
+
/// `ReadFile`, which fails with INVALID_PARAMETER on overlapped sockets when
118
118
+
/// passed a null OVERLAPPED. Returns 0 on graceful shutdown.
119
119
+
pub fn recv(s: ws2_32.SOCKET, buf: []u8) RecvError!usize {
120
120
+
const len: i32 = @intCast(@min(buf.len, std.math.maxInt(i32)));
121
121
+
const rc = ws2_32.recv(s, buf.ptr, len, 0);
122
122
+
if (rc != ws2_32.SOCKET_ERROR) return @intCast(rc);
123
123
+
switch (ws2_32.WSAGetLastError()) {
124
124
+
.WSAEWOULDBLOCK => return error.WouldBlock,
125
125
+
.WSAECONNRESET,
126
126
+
.WSAECONNABORTED,
127
127
+
.WSAENETRESET,
128
128
+
.WSAETIMEDOUT,
129
129
+
=> return error.ConnectionResetByPeer,
130
130
+
.WSAENETDOWN => return error.NetworkSubsystemFailed,
131
131
+
.WSAENOTCONN => return error.SocketNotConnected,
132
132
+
.WSAESHUTDOWN => return 0,
133
133
+
.WSAEINTR, .WSAEINPROGRESS => unreachable,
134
134
+
.WSAEINVAL, .WSAEFAULT => unreachable,
135
135
+
.WSAENOTSOCK => unreachable,
136
136
+
.WSAEOPNOTSUPP => unreachable,
137
137
+
else => |err| return unexpectedWSAError(err),
138
138
+
}
139
139
+
}
140
140
+
141
141
+
pub const SendError = error{
142
142
+
WouldBlock,
143
143
+
ConnectionResetByPeer,
144
144
+
BrokenPipe,
145
145
+
SocketNotConnected,
146
146
+
NetworkSubsystemFailed,
147
147
+
AccessDenied,
148
148
+
SystemResources,
149
149
+
MessageTooBig,
150
150
+
Unexpected,
151
151
+
};
152
152
+
153
153
+
/// Synchronous `send` for a `WSA_FLAG_OVERLAPPED` socket.
154
154
+
pub fn send(s: ws2_32.SOCKET, bytes: []const u8) SendError!usize {
155
155
+
const len: i32 = @intCast(@min(bytes.len, std.math.maxInt(i32)));
156
156
+
const rc = ws2_32.send(s, bytes.ptr, len, 0);
157
157
+
if (rc != ws2_32.SOCKET_ERROR) return @intCast(rc);
158
158
+
switch (ws2_32.WSAGetLastError()) {
159
159
+
.WSAEWOULDBLOCK => return error.WouldBlock,
160
160
+
.WSAECONNRESET, .WSAENETRESET, .WSAETIMEDOUT, .WSAEHOSTUNREACH => return error.ConnectionResetByPeer,
161
161
+
.WSAECONNABORTED, .WSAESHUTDOWN => return error.BrokenPipe,
162
162
+
.WSAENETDOWN => return error.NetworkSubsystemFailed,
163
163
+
.WSAENOTCONN => return error.SocketNotConnected,
164
164
+
.WSAEACCES => return error.AccessDenied,
165
165
+
.WSAENOBUFS => return error.SystemResources,
166
166
+
.WSAEMSGSIZE => return error.MessageTooBig,
167
167
+
.WSAEINTR, .WSAEINPROGRESS => unreachable,
168
168
+
.WSAEINVAL, .WSAEFAULT => unreachable,
169
169
+
.WSAENOTSOCK => unreachable,
170
170
+
.WSAEOPNOTSUPP => unreachable,
171
171
+
else => |err| return unexpectedWSAError(err),
172
172
+
}
173
173
+
}
174
174
+
108
175
pub const ReadFileError = error{
109
176
BrokenPipe,
110
177
/// The specified network name is no longer available.