io_interface: Mock netListenIp, netAccept, netConnectIp, netSend, netRead, netWrite
This commit is contained in:
+151
-32
@@ -1079,32 +1079,97 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) RandomSecureError!void {
|
|||||||
@panic("Not implemented yet");
|
@panic("Not implemented yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn nodeFromUserdata(userdata: ?*anyopaque) *Node {
|
||||||
|
return @ptrCast(@alignCast(userdata.?));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ipAddressToNodeAddress(address: *const net.IpAddress) ?Node.Address {
|
||||||
|
return switch (address.*) {
|
||||||
|
.ip4 => |ip4| .{
|
||||||
|
.ipv4 = std.mem.readInt(u32, &ip4.bytes, .big),
|
||||||
|
.port = ip4.port,
|
||||||
|
},
|
||||||
|
.ip6 => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nodeAddressToIpAddress(address: Node.Address) net.IpAddress {
|
||||||
|
var bytes: [4]u8 = undefined;
|
||||||
|
std.mem.writeInt(u32, &bytes, address.ipv4, .big);
|
||||||
|
return .{ .ip4 = .{
|
||||||
|
.bytes = bytes,
|
||||||
|
.port = address.port,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
fn netListenIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Socket {
|
fn netListenIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Socket {
|
||||||
_ = userdata;
|
const node = nodeFromUserdata(userdata);
|
||||||
_ = address;
|
|
||||||
_ = options;
|
if (options.mode != .stream)
|
||||||
@panic("Not implemented yet");
|
return error.SocketModeUnsupported;
|
||||||
|
if (options.protocol != .tcp)
|
||||||
|
return error.ProtocolUnsupportedBySystem;
|
||||||
|
|
||||||
|
const node_address = ipAddressToNodeAddress(address) orelse return error.AddressFamilyUnsupported;
|
||||||
|
const handle = node.listen(node_address) catch |err| switch (err) {
|
||||||
|
error.DescriptorLimit => return error.ProcessFdQuotaExceeded,
|
||||||
|
error.AddressNotAvailable => return error.AddressUnavailable,
|
||||||
|
error.AddressAlreadyUsed => return error.AddressInUse,
|
||||||
|
error.OutOfMemory => return error.SystemResources,
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.handle = handle,
|
||||||
|
.address = nodeAddressToIpAddress(node_address),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn netAccept(userdata: ?*anyopaque, listen_handle: net.Socket.Handle, options: net.Server.AcceptOptions) net.Server.AcceptError!net.Socket {
|
fn netAccept(userdata: ?*anyopaque, listen_handle: net.Socket.Handle, options: net.Server.AcceptOptions) net.Server.AcceptError!net.Socket {
|
||||||
_ = userdata;
|
const node = nodeFromUserdata(userdata);
|
||||||
_ = listen_handle;
|
|
||||||
_ = options;
|
_ = options;
|
||||||
@panic("Not implemented yet");
|
|
||||||
|
const handle = node.accept(listen_handle) catch |err| switch (err) {
|
||||||
|
error.DescriptorLimit => return error.ProcessFdQuotaExceeded,
|
||||||
|
error.InvalidHandle => return error.SocketNotListening,
|
||||||
|
error.AcceptQueueEmpty => return error.WouldBlock,
|
||||||
|
error.OutOfMemory => return error.SystemResources,
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.handle = handle,
|
||||||
|
.address = .{ .ip4 = .unspecified(0) },
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn netBindIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket {
|
fn netBindIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket {
|
||||||
_ = userdata;
|
_ = userdata;
|
||||||
_ = address;
|
_ = address;
|
||||||
_ = options;
|
_ = options;
|
||||||
@panic("Not implemented yet");
|
return error.OptionUnsupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn netConnectIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ConnectOptions) net.IpAddress.ConnectError!net.Socket {
|
fn netConnectIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ConnectOptions) net.IpAddress.ConnectError!net.Socket {
|
||||||
_ = userdata;
|
const node = nodeFromUserdata(userdata);
|
||||||
_ = address;
|
|
||||||
_ = options;
|
if (options.mode != .stream)
|
||||||
@panic("Not implemented yet");
|
return error.SocketModeUnsupported;
|
||||||
|
if (options.protocol) |protocol| {
|
||||||
|
if (protocol != .tcp)
|
||||||
|
return error.ProtocolUnsupportedBySystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
const node_address = ipAddressToNodeAddress(address) orelse return error.AddressFamilyUnsupported;
|
||||||
|
const handle = node.connect(node_address) catch |err| switch (err) {
|
||||||
|
error.DescriptorLimit => return error.ProcessFdQuotaExceeded,
|
||||||
|
error.UnavailableHost => return error.HostUnreachable,
|
||||||
|
error.PeerNotListeningOnAddress => return error.ConnectionRefused,
|
||||||
|
error.OutOfMemory => return error.SystemResources,
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.handle = handle,
|
||||||
|
.address = nodeAddressToIpAddress(node_address),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn netListenUnix(userdata: ?*anyopaque, address: *const net.UnixAddress, options: net.UnixAddress.ListenOptions) net.UnixAddress.ListenError!net.Socket.Handle {
|
fn netListenUnix(userdata: ?*anyopaque, address: *const net.UnixAddress, options: net.UnixAddress.ListenOptions) net.UnixAddress.ListenError!net.Socket.Handle {
|
||||||
@@ -1127,28 +1192,78 @@ fn netSocketCreatePair(userdata: ?*anyopaque, options: net.Socket.CreatePairOpti
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn netSend(userdata: ?*anyopaque, handle: net.Socket.Handle, messages: []net.OutgoingMessage, flags: net.SendFlags) struct { ?net.Socket.SendError, usize } {
|
fn netSend(userdata: ?*anyopaque, handle: net.Socket.Handle, messages: []net.OutgoingMessage, flags: net.SendFlags) struct { ?net.Socket.SendError, usize } {
|
||||||
_ = userdata;
|
const node = nodeFromUserdata(userdata);
|
||||||
_ = handle;
|
|
||||||
_ = messages;
|
|
||||||
_ = flags;
|
_ = flags;
|
||||||
@panic("Not implemented yet");
|
|
||||||
|
for (messages, 0..) |*message, index| {
|
||||||
|
const source = message.data_ptr[0..message.data_len];
|
||||||
|
const written = node.writeSocket(handle, source) catch |err| switch (err) {
|
||||||
|
error.InvalidHandle => return .{ error.SocketUnconnected, index },
|
||||||
|
error.NotConnected => return .{ error.SocketUnconnected, index },
|
||||||
|
error.OutOfMemory => return .{ error.SystemResources, index },
|
||||||
|
};
|
||||||
|
message.data_len = written;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ null, messages.len };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns 0 on end of stream.
|
/// Returns 0 on end of stream.
|
||||||
fn netRead(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
|
fn netRead(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
|
||||||
_ = userdata;
|
const node = nodeFromUserdata(userdata);
|
||||||
_ = fd;
|
var copied: usize = 0;
|
||||||
_ = data;
|
|
||||||
@panic("Not implemented yet");
|
for (data) |buffer| {
|
||||||
|
if (buffer.len == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const n = node.readSocket(fd, buffer) catch |err| switch (err) {
|
||||||
|
error.InvalidHandle => return error.SocketUnconnected,
|
||||||
|
};
|
||||||
|
copied += n;
|
||||||
|
|
||||||
|
if (n < buffer.len)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn netWrite(userdata: ?*anyopaque, handle: net.Socket.Handle, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize {
|
fn netWrite(userdata: ?*anyopaque, handle: net.Socket.Handle, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize {
|
||||||
_ = userdata;
|
const node = nodeFromUserdata(userdata);
|
||||||
_ = handle;
|
var copied: usize = 0;
|
||||||
_ = header;
|
|
||||||
_ = data;
|
if (header.len != 0)
|
||||||
_ = splat;
|
copied += node.writeSocket(handle, header) catch |err| switch (err) {
|
||||||
@panic("Not implemented yet");
|
error.InvalidHandle => return error.SocketUnconnected,
|
||||||
|
error.NotConnected => return error.SocketUnconnected,
|
||||||
|
error.OutOfMemory => return error.SystemResources,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.len != 0) {
|
||||||
|
for (data[0 .. data.len - 1]) |bytes| {
|
||||||
|
if (bytes.len == 0)
|
||||||
|
continue;
|
||||||
|
copied += node.writeSocket(handle, bytes) catch |err| switch (err) {
|
||||||
|
error.InvalidHandle => return error.SocketUnconnected,
|
||||||
|
error.NotConnected => return error.SocketUnconnected,
|
||||||
|
error.OutOfMemory => return error.SystemResources,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const pattern = data[data.len - 1];
|
||||||
|
for (0..splat) |_| {
|
||||||
|
if (pattern.len == 0)
|
||||||
|
break;
|
||||||
|
copied += node.writeSocket(handle, pattern) catch |err| switch (err) {
|
||||||
|
error.InvalidHandle => return error.SocketUnconnected,
|
||||||
|
error.NotConnected => return error.SocketUnconnected,
|
||||||
|
error.OutOfMemory => return error.SystemResources,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn netWriteFile(userdata: ?*anyopaque, socket_handle: net.Socket.Handle, header: []const u8, file_reader: *Io.File.Reader, limit: Io.Limit) net.Stream.Writer.WriteFileError!usize {
|
fn netWriteFile(userdata: ?*anyopaque, socket_handle: net.Socket.Handle, header: []const u8, file_reader: *Io.File.Reader, limit: Io.Limit) net.Stream.Writer.WriteFileError!usize {
|
||||||
@@ -1161,16 +1276,20 @@ fn netWriteFile(userdata: ?*anyopaque, socket_handle: net.Socket.Handle, header:
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
|
fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
|
||||||
_ = userdata;
|
const node = nodeFromUserdata(userdata);
|
||||||
_ = handles;
|
|
||||||
@panic("Not implemented yet");
|
for (handles) |handle| {
|
||||||
|
node.closeSocket(handle) catch {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn netShutdown(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
|
fn netShutdown(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
|
||||||
_ = userdata;
|
const node = nodeFromUserdata(userdata);
|
||||||
_ = handle;
|
|
||||||
_ = how;
|
_ = how;
|
||||||
@panic("Not implemented yet");
|
|
||||||
|
node.closeSocket(handle) catch |err| switch (err) {
|
||||||
|
error.InvalidHandle => return error.SocketUnconnected,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn netInterfaceNameResolve(userdata: ?*anyopaque, name: *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface {
|
fn netInterfaceNameResolve(userdata: ?*anyopaque, name: *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface {
|
||||||
|
|||||||
+1
-1
@@ -237,7 +237,7 @@ pub const Host = struct {
|
|||||||
socket.accept_queue.deinit(self.gpa);
|
socket.accept_queue.deinit(self.gpa);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send(_: *Host, socket: *ConnSocket, source: []const u8) usize {
|
pub fn send(_: *Host, socket: *ConnSocket, source: []const u8) SendError!usize {
|
||||||
const peer_conn = socket.peer_conn orelse return SendError.NotConnected;
|
const peer_conn = socket.peer_conn orelse return SendError.NotConnected;
|
||||||
try peer_conn.input_buffer.appendSlice(peer_conn.host.gpa, source);
|
try peer_conn.input_buffer.appendSlice(peer_conn.host.gpa, source);
|
||||||
return source.len;
|
return source.len;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const Simulator = @import("simulator.zig");
|
|||||||
|
|
||||||
const Io = std.Io;
|
const Io = std.Io;
|
||||||
const Clock = Io.Clock;
|
const Clock = Io.Clock;
|
||||||
|
const net = Io.net;
|
||||||
|
|
||||||
fn testClockGrowsMonotonically(init: std.process.Init) anyerror!void {
|
fn testClockGrowsMonotonically(init: std.process.Init) anyerror!void {
|
||||||
const io = init.io;
|
const io = init.io;
|
||||||
@@ -26,3 +27,72 @@ test "clock grows monotonically" {
|
|||||||
|
|
||||||
while (sim.scheduleOne()) {}
|
while (sim.scheduleOne()) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var networking_server_received = false;
|
||||||
|
var networking_client_received = false;
|
||||||
|
|
||||||
|
fn networkingServer(init: std.process.Init) anyerror!void {
|
||||||
|
const address: net.IpAddress = .{ .ip4 = .loopback(8080) };
|
||||||
|
|
||||||
|
var server = try address.listen(init.io, .{});
|
||||||
|
defer server.deinit(init.io);
|
||||||
|
|
||||||
|
var stream = try server.accept(init.io);
|
||||||
|
defer stream.close(init.io);
|
||||||
|
|
||||||
|
var reader_buffer: [16]u8 = undefined;
|
||||||
|
var reader = stream.reader(init.io, &reader_buffer);
|
||||||
|
var request: [4]u8 = undefined;
|
||||||
|
try reader.interface.readSliceAll(&request);
|
||||||
|
try std.testing.expectEqualStrings("ping", &request);
|
||||||
|
networking_server_received = true;
|
||||||
|
|
||||||
|
var writer_buffer: [16]u8 = undefined;
|
||||||
|
var writer = stream.writer(init.io, &writer_buffer);
|
||||||
|
try writer.interface.writeAll("pong");
|
||||||
|
try writer.interface.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn networkingClient(init: std.process.Init) anyerror!void {
|
||||||
|
const address: net.IpAddress = .{ .ip4 = .loopback(8080) };
|
||||||
|
|
||||||
|
var stream = try address.connect(init.io, .{ .mode = .stream });
|
||||||
|
defer stream.close(init.io);
|
||||||
|
|
||||||
|
var writer_buffer: [16]u8 = undefined;
|
||||||
|
var writer = stream.writer(init.io, &writer_buffer);
|
||||||
|
try writer.interface.writeAll("ping");
|
||||||
|
try writer.interface.flush();
|
||||||
|
|
||||||
|
try Io.sleep(init.io, .fromMilliseconds(1), .awake);
|
||||||
|
|
||||||
|
var reader_buffer: [16]u8 = undefined;
|
||||||
|
var reader = stream.reader(init.io, &reader_buffer);
|
||||||
|
var response: [4]u8 = undefined;
|
||||||
|
try reader.interface.readSliceAll(&response);
|
||||||
|
try std.testing.expectEqualStrings("pong", &response);
|
||||||
|
networking_client_received = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "networking mocks support tcp stream listen connect accept read write" {
|
||||||
|
networking_server_received = false;
|
||||||
|
networking_client_received = false;
|
||||||
|
|
||||||
|
const loopback = net.Ip4Address.loopback(8080);
|
||||||
|
const server_addresses = [_]u32{std.mem.readInt(u32, &loopback.bytes, .big)};
|
||||||
|
|
||||||
|
var sim: Simulator = undefined;
|
||||||
|
sim.init(std.heap.page_allocator, std.Options.debug_io);
|
||||||
|
defer sim.deinit();
|
||||||
|
|
||||||
|
try sim.addExecutable("server", networkingServer);
|
||||||
|
try sim.addExecutable("client", networkingClient);
|
||||||
|
|
||||||
|
try sim.spawn("server", .{ .addresses = &server_addresses });
|
||||||
|
try sim.spawn("client", .{});
|
||||||
|
|
||||||
|
while (sim.scheduleOne()) {}
|
||||||
|
|
||||||
|
try std.testing.expect(networking_server_received);
|
||||||
|
try std.testing.expect(networking_client_received);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user