Created
April 10, 2022 13:13
-
-
Save matu3ba/b742d68bf624a51b28d73396315f2a1a to your computer and use it in GitHub Desktop.
child_process with tcp connection for portability eventually be used as test_runner
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! client started by server as subprocess | |
const std = @import("std"); | |
const builtin = @import("builtin"); | |
const net = std.x.net; | |
const os = std.x.os; | |
const testing = std.testing; | |
const tcp = net.tcp; | |
const ip = net.ip; | |
const native_os = builtin.os; | |
const IPv4 = os.IPv4; | |
const IPv6 = os.IPv6; | |
const Socket = os.Socket; | |
const Buffer = os.Buffer; | |
// 1. server opens socket for client to connect | |
// 2. server --spawn child_process--> client with transmit ip address to connect to | |
// 3. client connects to socket | |
// 4. test stuff | |
pub fn main() !void { | |
const allocator = testing.allocator; | |
std.debug.print("client started\n", .{}); | |
//const std.process. | |
std.debug.print("pid\n", .{}); | |
_ = allocator; | |
var it = try std.process.argsWithAllocator(allocator); | |
defer it.deinit(); // no-op unless WASI or Windows | |
_ = it.next(); // ignore binary name | |
const str_ip = it.next().?; | |
const str_port = it.next().?; | |
try std.testing.expect(!it.skip()); | |
const client = try tcp.Client.init(.ip, .{ .close_on_exec = true }); | |
defer client.deinit(); | |
const ipv4 = try IPv4.parse(str_ip); | |
const port = try std.fmt.parseUnsigned(u16, str_port, 10); | |
const binded_address = ip.Address.initIPv4(ipv4, port); | |
try client.connect(binded_address); | |
const message = "hello world"; | |
var buf: [message.len + 1]u8 = undefined; | |
var msg = Socket.Message.fromBuffers(&[_]Buffer{ | |
Buffer.from(buf[0 .. message.len / 2]), | |
Buffer.from(buf[message.len / 2 ..]), | |
}); | |
_ = try client.readMessage(&msg, 0); | |
try testing.expectEqualStrings(message, buf[0..message.len]); | |
std.debug.print("comparison successful\n", .{}); | |
try client.shutdown(std.os.ShutdownHow.both); | |
std.debug.print("client exited\n", .{}); | |
} | |
// zig build-exe client.zig |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! server starts client as subprocess | |
const std = @import("std"); | |
const builtin = @import("builtin"); | |
const native_os = builtin.os; | |
const net = std.x.net; | |
const os = std.x.os; | |
const testing = std.testing; | |
const ChildProcess = std.ChildProcess; | |
const tcp = net.tcp; | |
const ip = net.ip; | |
const IPv4 = os.IPv4; | |
const IPv6 = os.IPv6; | |
const Socket = os.Socket; | |
const Buffer = os.Buffer; | |
const have_ifnamesize = @hasDecl(std.os.system, "IFNAMESIZE"); // interface namesize | |
// 1. server opens socket for client to connect | |
// 2. server --spawn child_process--> client + transmit ip address to connect to | |
// 3. client connects to socket | |
// 4. test stuff | |
// running on raw sockets is not possible without additional permissions | |
// https://squidarth.com/networking/systems/rc/2018/05/28/using-raw-sockets.html | |
// only alternative: tcp/udp | |
pub fn main() !void { | |
if (!have_ifnamesize) return error.FAILURE; | |
const allocator = testing.allocator; | |
std.debug.print("server started\n", .{}); | |
const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true }); | |
defer listener.deinit(); | |
try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); | |
try listener.listen(128); | |
var binded_address = try listener.getLocalAddress(); | |
switch (binded_address) { | |
.ipv4 => |*ipv4| ipv4.host = IPv4.localhost, | |
.ipv6 => |*ipv6| ipv6.host = IPv6.localhost, | |
} | |
// max port nr IPv4: unsigned 16-bit integer, so 65535 (5 digits) | |
// max ip nr IPv4: 255.255.255.255 => 12 digits + 3 dots + : 1 => 16 digits | |
std.debug.print("address: {s}\n", .{binded_address.ipv4.host}); | |
const str_ip = try std.fmt.allocPrint(allocator, "{s}", .{binded_address.ipv4.host}); | |
const str_port = try std.fmt.allocPrint(allocator, "{d}", .{binded_address.ipv4.port}); | |
std.debug.print("{s}\n", .{str_ip}); | |
std.debug.print("{s}\n", .{str_port}); | |
const ipv4 = try IPv4.parse(str_ip); | |
const port = try std.fmt.parseUnsigned(u16, str_port, 10); | |
std.debug.print("{s}\n", .{ipv4}); | |
std.debug.print("{d}\n", .{port}); | |
const cwd_path = try std.process.getCwdAlloc(allocator); | |
std.debug.print("cwd_path: {s}\n", .{cwd_path}); | |
// TODO absolute paths | |
const args = [_][]const u8{ "./client", str_ip, str_port }; | |
var child_proc = try ChildProcess.init(&args, allocator); | |
defer child_proc.deinit(); | |
try child_proc.spawn(); | |
std.debug.print("child_proc spawned:\n", .{}); | |
std.debug.print("args {s}\n", .{args}); | |
const conn = try listener.accept(.{ .close_on_exec = true }); // accept is blocking | |
defer conn.deinit(); | |
std.debug.print("connection succesful\n", .{}); | |
const message = "hello world"; | |
_ = try conn.client.writeMessage(Socket.Message.fromBuffers(&[_]Buffer{ | |
Buffer.from(message[0 .. message.len / 2]), | |
Buffer.from(message[message.len / 2 ..]), | |
}), 0); | |
// server initiates shutdown before waiting on client | |
// reason: client process may not finish => we do not get stdout/stderr and shutdown | |
try listener.shutdown(); | |
const ret_val = child_proc.wait(); | |
try testing.expectEqual(ret_val, .{ .Exited = 0 }); | |
std.debug.print("server exited\n", .{}); | |
} | |
// zig build-exe server.zig | |
// run with ./server |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment