authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-10-27 15:37:28+01:00
committergravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-10-27 21:52:47+01:00
log8044ed4c66840503186fc3bbeac4328c6cf76349
treee2292041959ea606e7f0d97b087d11a428534aa6
parent194e29adfccbd5076a3523572c7e119d4bfa647d

std: Add basic smoke test for net functionality


7 files changed, 193 insertions(+), 34 deletions(-)

lib/std/net.zig+17-11
......@@ -11,11 +11,7 @@ const mem = std.mem;
1111const os = std.os;
1212const fs = std.fs;
1313
14test "" {
15 _ = @import("net/test.zig");
16}
17
18const has_unix_sockets = @hasDecl(os, "sockaddr_un");
14pub const has_unix_sockets = @hasDecl(os, "sockaddr_un");
1915
2016pub const Address = extern union {
2117 any: os.sockaddr,
......@@ -1546,16 +1542,14 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)
15461542 if (data.len != 4) return error.InvalidDnsARecord;
15471543 const new_addr = try ctx.addrs.addOne();
15481544 new_addr.* = LookupAddr{
1549 // TODO slice [0..4] to make this *[4]u8 without @ptrCast
1550 .addr = Address.initIp4(@ptrCast(*const [4]u8, data.ptr).*, ctx.port),
1545 .addr = Address.initIp4(data[0..4].*, ctx.port),
15511546 };
15521547 },
15531548 os.RR_AAAA => {
15541549 if (data.len != 16) return error.InvalidDnsAAAARecord;
15551550 const new_addr = try ctx.addrs.addOne();
15561551 new_addr.* = LookupAddr{
1557 // TODO slice [0..16] to make this *[16]u8 without @ptrCast
1558 .addr = Address.initIp6(@ptrCast(*const [16]u8, data.ptr).*, ctx.port, 0, 0),
1552 .addr = Address.initIp6(data[0..16].*, ctx.port, 0, 0),
15591553 };
15601554 },
15611555 os.RR_CNAME => {
......@@ -1579,7 +1573,7 @@ pub const StreamServer = struct {
15791573 /// `undefined` until `listen` returns successfully.
15801574 listen_address: Address,
15811575
1582 sockfd: ?os.fd_t,
1576 sockfd: ?os.socket_t,
15831577
15841578 pub const Options = struct {
15851579 /// How many connections the kernel will accept on the application's behalf.
......@@ -1622,7 +1616,7 @@ pub const StreamServer = struct {
16221616
16231617 if (self.reuse_address) {
16241618 try os.setsockopt(
1625 self.sockfd.?,
1619 sockfd,
16261620 os.SOL_SOCKET,
16271621 os.SO_REUSEADDR,
16281622 &mem.toBytes(@as(c_int, 1)),
......@@ -1670,6 +1664,14 @@ pub const StreamServer = struct {
16701664 /// Permission to create a socket of the specified type and/or
16711665 /// protocol is denied.
16721666 PermissionDenied,
1667
1668 FileDescriptorNotASocket,
1669
1670 ConnectionResetByPeer,
1671
1672 NetworkSubsystemFailed,
1673
1674 OperationNotSupported,
16731675 } || os.UnexpectedError;
16741676
16751677 pub const Connection = struct {
......@@ -1701,3 +1703,7 @@ pub const StreamServer = struct {
17011703 }
17021704 }
17031705};
1706
1707test "" {
1708 _ = @import("net/test.zig");
1709}
lib/std/net/test.zig+65-8
......@@ -95,22 +95,79 @@ test "parse and render IPv4 addresses" {
9595}
9696
9797test "resolve DNS" {
98 if (builtin.os.tag == .wasi) return error.SkipZigTest;
99
98100 if (std.builtin.os.tag == .windows) {
99101 _ = try std.os.windows.WSAStartup(2, 2);
100102 }
101 if (builtin.os.tag == .wasi) {
102 // DNS resolution not implemented on Windows yet.
103 return error.SkipZigTest;
103 defer {
104 if (std.builtin.os.tag == .windows) {
105 std.os.windows.WSACleanup() catch unreachable;
106 }
107 }
108
109 // Resolve localhost, this should not fail.
110 {
111 const localhost_v4 = try net.Address.parseIp("127.0.0.1", 80);
112 const localhost_v6 = try net.Address.parseIp("::2", 80);
113
114 const result = try net.getAddressList(testing.allocator, "localhost", 80);
115 defer result.deinit();
116 for (result.addrs) |addr| {
117 if (addr.eql(localhost_v4) or addr.eql(localhost_v6)) break;
118 } else @panic("unexpected address for localhost");
104119 }
105120
106 const address_list = net.getAddressList(testing.allocator, "example.com", 80) catch |err| switch (err) {
121 {
107122 // The tests are required to work even when there is no Internet connection,
108123 // so some of these errors we must accept and skip the test.
109 error.UnknownHostName => return error.SkipZigTest,
110 error.TemporaryNameServerFailure => return error.SkipZigTest,
111 else => return err,
124 const result = net.getAddressList(testing.allocator, "example.com", 80) catch |err| switch (err) {
125 error.UnknownHostName => return error.SkipZigTest,
126 error.TemporaryNameServerFailure => return error.SkipZigTest,
127 else => return err,
128 };
129 result.deinit();
130 }
131}
132
133test "listen on a port, send bytes, receive bytes" {
134 if (builtin.single_threaded) return error.SkipZigTest;
135 if (builtin.os.tag == .wasi) return error.SkipZigTest;
136
137 if (std.builtin.os.tag == .windows) {
138 _ = try std.os.windows.WSAStartup(2, 2);
139 }
140 defer {
141 if (std.builtin.os.tag == .windows) {
142 std.os.windows.WSACleanup() catch unreachable;
143 }
144 }
145
146 // Try only the IPv4 variant as some CI builders have no IPv6 localhost
147 // configured.
148 const localhost = try net.Address.parseIp("127.0.0.1", 8080);
149
150 var server = net.StreamServer.init(.{});
151 try server.listen(localhost);
152
153 const S = struct {
154 fn clientFn(server_address: net.Address) !void {
155 const socket = try net.tcpConnectToAddress(server_address);
156 defer socket.close();
157
158 _ = try socket.writer().writeAll("Hello world!");
159 }
112160 };
113 address_list.deinit();
161
162 const t = try std.Thread.spawn(server.listen_address, S.clientFn);
163 defer t.wait();
164
165 var client = try server.accept();
166 var buf: [16]u8 = undefined;
167 const n = try client.file.reader().read(&buf);
168
169 testing.expectEqual(@as(usize, 12), n);
170 testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
114171}
115172
116173test "listen on a port, send bytes, receive bytes" {
lib/std/os.zig+32-13
......@@ -5378,22 +5378,41 @@ pub const SetSockOptError = error{
53785378
53795379 /// Insufficient resources are available in the system to complete the call.
53805380 SystemResources,
5381
5382 NetworkSubsystemFailed,
5383 FileDescriptorNotASocket,
5384 SocketNotBound,
53815385} || UnexpectedError;
53825386
53835387/// Set a socket's options.
5384pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void {
5385 switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) {
5386 0 => {},
5387 EBADF => unreachable, // always a race condition
5388 ENOTSOCK => unreachable, // always a race condition
5389 EINVAL => unreachable,
5390 EFAULT => unreachable,
5391 EDOM => return error.TimeoutTooBig,
5392 EISCONN => return error.AlreadyConnected,
5393 ENOPROTOOPT => return error.InvalidProtocolOption,
5394 ENOMEM => return error.SystemResources,
5395 ENOBUFS => return error.SystemResources,
5396 else => |err| return unexpectedErrno(err),
5388pub fn setsockopt(fd: socket_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void {
5389 if (builtin.os.tag == .windows) {
5390 const rc = windows.ws2_32.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len));
5391 if (rc == windows.ws2_32.SOCKET_ERROR) {
5392 switch (windows.ws2_32.WSAGetLastError()) {
5393 .WSANOTINITIALISED => unreachable,
5394 .WSAENETDOWN => return error.NetworkSubsystemFailed,
5395 .WSAEFAULT => unreachable,
5396 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
5397 .WSAEINVAL => return error.SocketNotBound,
5398 else => |err| return windows.unexpectedWSAError(err),
5399 }
5400 }
5401 return;
5402 } else {
5403 switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) {
5404 0 => {},
5405 EBADF => unreachable, // always a race condition
5406 ENOTSOCK => unreachable, // always a race condition
5407 EINVAL => unreachable,
5408 EFAULT => unreachable,
5409 EDOM => return error.TimeoutTooBig,
5410 EISCONN => return error.AlreadyConnected,
5411 ENOPROTOOPT => return error.InvalidProtocolOption,
5412 ENOMEM => return error.SystemResources,
5413 ENOBUFS => return error.SystemResources,
5414 else => |err| return unexpectedErrno(err),
5415 }
53975416 }
53985417}
53995418
lib/std/os/bits/windows.zig+35
......@@ -256,6 +256,41 @@ pub const POLLERR = ws2_32.POLLERR;
256256pub const POLLHUP = ws2_32.POLLHUP;
257257pub const POLLNVAL = ws2_32.POLLNVAL;
258258
259pub const SOL_SOCKET = ws2_32.SOL_SOCKET;
260
261pub const SO_DEBUG = ws2_32.SO_DEBUG;
262pub const SO_ACCEPTCONN = ws2_32.SO_ACCEPTCONN;
263pub const SO_REUSEADDR = ws2_32.SO_REUSEADDR;
264pub const SO_KEEPALIVE = ws2_32.SO_KEEPALIVE;
265pub const SO_DONTROUTE = ws2_32.SO_DONTROUTE;
266pub const SO_BROADCAST = ws2_32.SO_BROADCAST;
267pub const SO_USELOOPBACK = ws2_32.SO_USELOOPBACK;
268pub const SO_LINGER = ws2_32.SO_LINGER;
269pub const SO_OOBINLINE = ws2_32.SO_OOBINLINE;
270
271pub const SO_DONTLINGER = ws2_32.SO_DONTLINGER;
272pub const SO_EXCLUSIVEADDRUSE = ws2_32.SO_EXCLUSIVEADDRUSE;
273
274pub const SO_SNDBUF = ws2_32.SO_SNDBUF;
275pub const SO_RCVBUF = ws2_32.SO_RCVBUF;
276pub const SO_SNDLOWAT = ws2_32.SO_SNDLOWAT;
277pub const SO_RCVLOWAT = ws2_32.SO_RCVLOWAT;
278pub const SO_SNDTIMEO = ws2_32.SO_SNDTIMEO;
279pub const SO_RCVTIMEO = ws2_32.SO_RCVTIMEO;
280pub const SO_ERROR = ws2_32.SO_ERROR;
281pub const SO_TYPE = ws2_32.SO_TYPE;
282
283pub const SO_GROUP_ID = ws2_32.SO_GROUP_ID;
284pub const SO_GROUP_PRIORITY = ws2_32.SO_GROUP_PRIORITY;
285pub const SO_MAX_MSG_SIZE = ws2_32.SO_MAX_MSG_SIZE;
286pub const SO_PROTOCOL_INFOA = ws2_32.SO_PROTOCOL_INFOA;
287pub const SO_PROTOCOL_INFOW = ws2_32.SO_PROTOCOL_INFOW;
288
289pub const PVD_CONFIG = ws2_32.PVD_CONFIG;
290pub const SO_CONDITIONAL_ACCEPT = ws2_32.SO_CONDITIONAL_ACCEPT;
291
292pub const TCP_NODELAY = ws2_32.TCP_NODELAY;
293
259294pub const O_RDONLY = 0o0;
260295pub const O_WRONLY = 0o1;
261296pub const O_RDWR = 0o2;
lib/std/os/linux.zig+1-1
......@@ -1279,7 +1279,7 @@ pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit,
12791279 @bitCast(usize, @as(isize, pid)),
12801280 @bitCast(usize, @as(isize, @enumToInt(resource))),
12811281 @ptrToInt(new_limit),
1282 @ptrToInt(old_limit)
1282 @ptrToInt(old_limit),
12831283 );
12841284}
12851285
lib/std/os/test.zig+1-1
......@@ -594,7 +594,7 @@ test "fsync" {
594594
595595test "getrlimit and setrlimit" {
596596 // TODO enable for other systems when implemented
597 if(builtin.os.tag != .linux){
597 if (builtin.os.tag != .linux) {
598598 return error.SkipZigTest;
599599 }
600600
lib/std/os/windows/ws2_32.zig+42
......@@ -718,6 +718,41 @@ const IOC_WS2 = 0x08000000;
718718
719719pub const SIO_BASE_HANDLE = IOC_OUT | IOC_WS2 | 34;
720720
721pub const SOL_SOCKET = 0xffff;
722
723pub const SO_DEBUG = 0x0001;
724pub const SO_ACCEPTCONN = 0x0002;
725pub const SO_REUSEADDR = 0x0004;
726pub const SO_KEEPALIVE = 0x0008;
727pub const SO_DONTROUTE = 0x0010;
728pub const SO_BROADCAST = 0x0020;
729pub const SO_USELOOPBACK = 0x0040;
730pub const SO_LINGER = 0x0080;
731pub const SO_OOBINLINE = 0x0100;
732
733pub const SO_DONTLINGER = ~@as(u32, SO_LINGER);
734pub const SO_EXCLUSIVEADDRUSE = ~@as(u32, SO_REUSEADDR);
735
736pub const SO_SNDBUF = 0x1001;
737pub const SO_RCVBUF = 0x1002;
738pub const SO_SNDLOWAT = 0x1003;
739pub const SO_RCVLOWAT = 0x1004;
740pub const SO_SNDTIMEO = 0x1005;
741pub const SO_RCVTIMEO = 0x1006;
742pub const SO_ERROR = 0x1007;
743pub const SO_TYPE = 0x1008;
744
745pub const SO_GROUP_ID = 0x2001;
746pub const SO_GROUP_PRIORITY = 0x2002;
747pub const SO_MAX_MSG_SIZE = 0x2003;
748pub const SO_PROTOCOL_INFOA = 0x2004;
749pub const SO_PROTOCOL_INFOW = 0x2005;
750
751pub const PVD_CONFIG = 0x3001;
752pub const SO_CONDITIONAL_ACCEPT = 0x3002;
753
754pub const TCP_NODELAY = 0x0001;
755
721756pub extern "ws2_32" fn WSAStartup(
722757 wVersionRequired: WORD,
723758 lpWSAData: *WSADATA,
......@@ -835,3 +870,10 @@ pub extern "ws2_32" fn getsockname(
835870 name: *sockaddr,
836871 namelen: *c_int,
837872) callconv(.Stdcall) c_int;
873pub extern "ws2_32" fn setsockopt(
874 s: SOCKET,
875 level: u32,
876 optname: u32,
877 optval: ?*const c_void,
878 optlen: socklen_t,
879) callconv(.Stdcall) c_int;