From 983f93c84061c667b9286074428d0a0aecaddb15 Mon Sep 17 00:00:00 2001 From: Ian Simonson Date: Sun, 7 Jun 2020 22:36:35 +1000 Subject: [PATCH 1/2] Test case for tcpConnectToHost fix --- lib/std/net/test.zig | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 5327e88ffcec3ee8bff759f2a0ded8ad576634b6..89b84ac83dd3d074ac68d8e078ead31099ed5c99 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -130,6 +130,44 @@ test "listen on a port, send bytes, receive bytes" { try await client_frame; } +test "listen on ipv4 try connect on ipv6 then ipv4" { + if (!std.io.is_async) return error.SkipZigTest; + + if (std.builtin.os.tag != .linux and !std.builtin.os.tag.isDarwin()) { + // TODO build abstractions for other operating systems + return error.SkipZigTest; + } + + // TODO doing this at comptime crashed the compiler + const localhost = try net.Address.parseIp("127.0.0.1", 0); + + var server = net.StreamServer.init(net.StreamServer.Options{}); + defer server.deinit(); + try server.listen(localhost); + + var server_frame = async testServer(&server); + var client_frame = async testClientToHost( + testing.allocator, + "localhost", + server.listen_address.getPort(), + ); + + try await server_frame; + try await client_frame; +} + +fn testClientToHost(allocator: *mem.Allocator, name: []const u8, port: u16) anyerror!void { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const connection = try net.tcpConnectToHost(allocator, name, port); + defer connection.close(); + + var buf: [100]u8 = undefined; + const len = try connection.read(&buf); + const msg = buf[0..len]; + testing.expect(mem.eql(u8, msg, "hello from server\n")); +} + fn testClient(addr: net.Address) anyerror!void { if (builtin.os.tag == .wasi) return error.SkipZigTest; -- 2.54.0 From a6d1ef64d753f8be59d68695f927e03777bb6514 Mon Sep 17 00:00:00 2001 From: Ian Simonson Date: Sun, 7 Jun 2020 21:04:15 +1000 Subject: [PATCH 2/2] tcpConnectToHost try all addresses in AddressList The AddressList returned can contain more than one item e.g. the ipv4 and ipv6 addresses for a given hostname. Previously if a server had multiple addresses but was not listening on one of them Zig would give up immediately. Now on std.os.ConnectError.ConnectionRefused Zig will try the next address in the list. Zig still gives up on all other errors as they are related to the system and system resources rather than whether the remote server is listening on a particular address. --- lib/std/net.zig | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 34af916d13fd1e6a535cabf48c20d4b310f5a4dd..919175e41d0959b013361ca6cf6506d76ddadd0e 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -573,7 +573,15 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16) if (list.addrs.len == 0) return error.UnknownHostName; - return tcpConnectToAddress(list.addrs[0]); + for (list.addrs) |addr| { + return tcpConnectToAddress(addr) catch |err| switch (err) { + error.ConnectionRefused => { + continue; + }, + else => return err, + }; + } + return std.os.ConnectError.ConnectionRefused; } pub fn tcpConnectToAddress(address: Address) !fs.File { -- 2.54.0