Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 60 additions & 2 deletions src/backend/iocp.zig
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ pub const Loop = struct {
break :action switch (err) {
windows.ws2_32.WinsockError.WSA_IO_PENDING => .{ .submitted = {} },
.WSA_OPERATION_ABORTED, .WSAECONNABORTED => .{ .result = .{ .send = error.Canceled } },
.WSAECONNRESET, .WSAENETRESET => .{ .result = .{ .send = error.ConnectionResetByPeer } },
.WSAECONNRESET, .WSAENETRESET, .WSAESHUTDOWN => .{ .result = .{ .send = error.ConnectionResetByPeer } },
else => .{ .result = .{ .send = windows.unexpectedWSAError(err) } },
};
}
Expand Down Expand Up @@ -663,6 +663,7 @@ pub const Loop = struct {
windows.ws2_32.WinsockError.WSA_IO_PENDING => .{ .submitted = {} },
.WSA_OPERATION_ABORTED, .WSAECONNABORTED => .{ .result = .{ .recv = error.Canceled } },
.WSAECONNRESET, .WSAENETRESET => .{ .result = .{ .recv = error.ConnectionResetByPeer } },
.WSAESHUTDOWN => .{ .result = .{ .recv = error.EOF } },
else => .{ .result = .{ .recv = windows.unexpectedWSAError(err) } },
};
}
Expand Down Expand Up @@ -1095,7 +1096,7 @@ pub const Completion = struct {
return .{
.send = switch (err) {
.WSA_OPERATION_ABORTED, .WSAECONNABORTED => error.Canceled,
.WSAECONNRESET, .WSAENETRESET => error.ConnectionResetByPeer,
.WSAECONNRESET, .WSAENETRESET, .WSAESHUTDOWN => error.ConnectionResetByPeer,
else => windows.unexpectedWSAError(err),
},
};
Expand All @@ -1115,6 +1116,7 @@ pub const Completion = struct {
.recv = switch (err) {
.WSA_OPERATION_ABORTED, .WSAECONNABORTED => error.Canceled,
.WSAECONNRESET, .WSAENETRESET => error.ConnectionResetByPeer,
.WSAESHUTDOWN => error.EOF,
else => windows.unexpectedWSAError(err),
},
};
Expand Down Expand Up @@ -2012,6 +2014,62 @@ test "iocp: file IO with offset" {
try testing.expectEqualSlices(u8, &write_buf, read_buf[0..read_len]);
}

test "iocp: recv and send on a socket that was shut down" {
const mem = std.mem;
const testing = std.testing;

var loop = try Loop.init(.{});
defer loop.deinit();

const address = try net.Address.parseIp4("127.0.0.1", 3133);
const ln = try windows.WSASocketW(windows.ws2_32.AF.INET, windows.ws2_32.SOCK.STREAM, windows.ws2_32.IPPROTO.TCP, null, 0, windows.ws2_32.WSA_FLAG_OVERLAPPED);
defer iocpClose(ln);
try iocpSetsockopt(asSocket(ln), windows.ws2_32.SOL.SOCKET, windows.ws2_32.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
try iocpBind(asSocket(ln), &address.any, address.getOsSockLen());
try iocpListen(asSocket(ln), 1);

const conn = try windows.WSASocketW(windows.ws2_32.AF.INET, windows.ws2_32.SOCK.STREAM, windows.ws2_32.IPPROTO.TCP, null, 0, windows.ws2_32.WSA_FLAG_OVERLAPPED);
defer iocpClose(conn);
try testing.expectEqual(0, windows.ws2_32.connect(asSocket(conn), &address.any, @intCast(address.getOsSockLen())));
try iocpShutdown(asSocket(conn), .both);

const Outcome = struct {
recv: ?ReadError!usize = null,
send: ?WriteError!usize = null,

fn onRecv(ud: ?*anyopaque, _: *Loop, _: *Completion, r: Result) CallbackAction {
const self: *@This() = @ptrCast(@alignCast(ud.?));
self.recv = r.recv;
return .disarm;
}

fn onSend(ud: ?*anyopaque, _: *Loop, _: *Completion, r: Result) CallbackAction {
const self: *@This() = @ptrCast(@alignCast(ud.?));
self.send = r.send;
return .disarm;
}
};

var outcome: Outcome = .{};
var buf: [8]u8 = undefined;
var c_recv: Completion = .{
.op = .{ .recv = .{ .fd = conn, .buffer = .{ .slice = &buf } } },
.userdata = &outcome,
.callback = Outcome.onRecv,
};
var c_send: Completion = .{
.op = .{ .send = .{ .fd = conn, .buffer = .{ .slice = "ping" } } },
.userdata = &outcome,
.callback = Outcome.onSend,
};
loop.add(&c_recv);
loop.add(&c_send);
try loop.run(.until_done);

try testing.expectError(error.EOF, outcome.recv.?);
try testing.expectError(error.ConnectionResetByPeer, outcome.send.?);
}

test "iocp: socket accept/connect/send/recv/close" {
const mem = std.mem;
const testing = std.testing;
Expand Down
4 changes: 3 additions & 1 deletion src/windows.zig
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,9 @@ pub const ws2_32 = struct {
// --- High-level wrapper functions ---

pub fn unexpectedWSAError(err: ws2_32.WinsockError) error{Unexpected} {
return unexpectedError(@as(Win32Error, @enumFromInt(@intFromEnum(err))));
// Not `unexpectedError`: WSA codes aren't in `Win32Error`, and casting one panics.
std.log.debug("unexpected Winsock error: {d}", .{@intFromEnum(err)});
return error.Unexpected;
}

pub fn QueryPerformanceCounter() u64 {
Expand Down