| author | |
| committer | |
| log | 3b346223681b40ce1271688cbe4d650a43b8e613 |
| tree | 9ebfebd19c17f345d5c483806a55127e5589f687 |
| parent | 9e681cab56eb306f3e5ba88a951625408f72f696 |
note that "reuseaddr" does nothing for these4 files changed, 77 insertions(+), 42 deletions(-)
lib/std/Io.zig+6-4| ... | @@ -672,10 +672,12 @@ pub const VTable = struct { | ... | @@ -672,10 +672,12 @@ pub const VTable = struct { |
| 672 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, | 672 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, |
| 673 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, | 673 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, |
| 674 | 674 | ||
| 675 | listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server, | 675 | netListenIp: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server, |
| 676 | accept: *const fn (?*anyopaque, server: *net.Server) net.Server.AcceptError!net.Stream, | 676 | netAccept: *const fn (?*anyopaque, server: net.Socket.Handle) net.Server.AcceptError!net.Stream, |
| 677 | ipBind: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket, | 677 | netBindIp: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket, |
| 678 | ipConnect: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ConnectOptions) net.IpAddress.ConnectError!net.Stream, | 678 | netConnectIp: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ConnectOptions) net.IpAddress.ConnectError!net.Stream, |
| 679 | netListenUnix: *const fn (?*anyopaque, net.UnixAddress) net.UnixAddress.ListenError!net.Socket.Handle, | ||
| 680 | netConnectUnix: *const fn (?*anyopaque, net.UnixAddress) net.UnixAddress.ConnectError!net.Socket.Handle, | ||
| 679 | netSend: *const fn (?*anyopaque, net.Socket.Handle, []net.OutgoingMessage, net.SendFlags) struct { ?net.Socket.SendError, usize }, | 681 | netSend: *const fn (?*anyopaque, net.Socket.Handle, []net.OutgoingMessage, net.SendFlags) struct { ?net.Socket.SendError, usize }, |
| 680 | netReceive: *const fn (?*anyopaque, net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, net.ReceiveFlags, Timeout) struct { ?net.Socket.ReceiveTimeoutError, usize }, | 682 | netReceive: *const fn (?*anyopaque, net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, net.ReceiveFlags, Timeout) struct { ?net.Socket.ReceiveTimeoutError, usize }, |
| 681 | netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize, | 683 | netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize, |
lib/std/Io/Threaded.zig+28-13| ... | @@ -203,22 +203,24 @@ pub fn io(pool: *Pool) Io { | ... | @@ -203,22 +203,24 @@ pub fn io(pool: *Pool) Io { |
| 203 | else => sleepPosix, | 203 | else => sleepPosix, |
| 204 | }, | 204 | }, |
| 205 | 205 | ||
| 206 | .listen = switch (builtin.os.tag) { | 206 | .netListenIp = switch (builtin.os.tag) { |
| 207 | .windows => @panic("TODO"), | 207 | .windows => @panic("TODO"), |
| 208 | else => listenPosix, | 208 | else => netListenIpPosix, |
| 209 | }, | 209 | }, |
| 210 | .accept = switch (builtin.os.tag) { | 210 | .netListenUnix = netListenUnix, |
| 211 | .netAccept = switch (builtin.os.tag) { | ||
| 211 | .windows => @panic("TODO"), | 212 | .windows => @panic("TODO"), |
| 212 | else => acceptPosix, | 213 | else => netAcceptPosix, |
| 213 | }, | 214 | }, |
| 214 | .ipBind = switch (builtin.os.tag) { | 215 | .netBindIp = switch (builtin.os.tag) { |
| 215 | .windows => @panic("TODO"), | 216 | .windows => @panic("TODO"), |
| 216 | else => ipBindPosix, | 217 | else => netBindIpPosix, |
| 217 | }, | 218 | }, |
| 218 | .ipConnect = switch (builtin.os.tag) { | 219 | .netConnectIp = switch (builtin.os.tag) { |
| 219 | .windows => @panic("TODO"), | 220 | .windows => @panic("TODO"), |
| 220 | else => ipConnectPosix, | 221 | else => netConnectIpPosix, |
| 221 | }, | 222 | }, |
| 223 | .netConnectUnix = netConnectUnix, | ||
| 222 | .netClose = netClose, | 224 | .netClose = netClose, |
| 223 | .netRead = switch (builtin.os.tag) { | 225 | .netRead = switch (builtin.os.tag) { |
| 224 | .windows => @panic("TODO"), | 226 | .windows => @panic("TODO"), |
| ... | @@ -1636,7 +1638,7 @@ fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { | ... | @@ -1636,7 +1638,7 @@ fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { |
| 1636 | return result.?; | 1638 | return result.?; |
| 1637 | } | 1639 | } |
| 1638 | 1640 | ||
| 1639 | fn listenPosix( | 1641 | fn netListenIpPosix( |
| 1640 | userdata: ?*anyopaque, | 1642 | userdata: ?*anyopaque, |
| 1641 | address: Io.net.IpAddress, | 1643 | address: Io.net.IpAddress, |
| 1642 | options: Io.net.IpAddress.ListenOptions, | 1644 | options: Io.net.IpAddress.ListenOptions, |
| ... | @@ -1702,6 +1704,13 @@ fn listenPosix( | ... | @@ -1702,6 +1704,13 @@ fn listenPosix( |
| 1702 | }; | 1704 | }; |
| 1703 | } | 1705 | } |
| 1704 | 1706 | ||
| 1707 | fn netListenUnix(userdata: ?*anyopaque, address: Io.net.UnixAddress) Io.net.UnixAddress.ListenError!Io.net.Socket.Handle { | ||
| 1708 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | ||
| 1709 | _ = pool; | ||
| 1710 | _ = address; | ||
| 1711 | @panic("TODO"); | ||
| 1712 | } | ||
| 1713 | |||
| 1705 | fn posixBind(pool: *Pool, socket_fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void { | 1714 | fn posixBind(pool: *Pool, socket_fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void { |
| 1706 | while (true) { | 1715 | while (true) { |
| 1707 | try pool.checkCancel(); | 1716 | try pool.checkCancel(); |
| ... | @@ -1784,7 +1793,7 @@ fn setSocketOption(pool: *Pool, fd: posix.fd_t, level: i32, opt_name: u32, optio | ... | @@ -1784,7 +1793,7 @@ fn setSocketOption(pool: *Pool, fd: posix.fd_t, level: i32, opt_name: u32, optio |
| 1784 | } | 1793 | } |
| 1785 | } | 1794 | } |
| 1786 | 1795 | ||
| 1787 | fn ipConnectPosix( | 1796 | fn netConnectIpPosix( |
| 1788 | userdata: ?*anyopaque, | 1797 | userdata: ?*anyopaque, |
| 1789 | address: *const Io.net.IpAddress, | 1798 | address: *const Io.net.IpAddress, |
| 1790 | options: Io.net.IpAddress.ConnectOptions, | 1799 | options: Io.net.IpAddress.ConnectOptions, |
| ... | @@ -1806,7 +1815,14 @@ fn ipConnectPosix( | ... | @@ -1806,7 +1815,14 @@ fn ipConnectPosix( |
| 1806 | } }; | 1815 | } }; |
| 1807 | } | 1816 | } |
| 1808 | 1817 | ||
| 1809 | fn ipBindPosix( | 1818 | fn netConnectUnix(userdata: ?*anyopaque, address: Io.net.UnixAddress) Io.net.UnixAddress.ConnectError!Io.net.Socket.Handle { |
| 1819 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | ||
| 1820 | _ = pool; | ||
| 1821 | _ = address; | ||
| 1822 | @panic("TODO"); | ||
| 1823 | } | ||
| 1824 | |||
| 1825 | fn netBindIpPosix( | ||
| 1810 | userdata: ?*anyopaque, | 1826 | userdata: ?*anyopaque, |
| 1811 | address: *const Io.net.IpAddress, | 1827 | address: *const Io.net.IpAddress, |
| 1812 | options: Io.net.IpAddress.BindOptions, | 1828 | options: Io.net.IpAddress.BindOptions, |
| ... | @@ -1871,9 +1887,8 @@ fn openSocketPosix(pool: *Pool, family: posix.sa_family_t, options: Io.net.IpAdd | ... | @@ -1871,9 +1887,8 @@ fn openSocketPosix(pool: *Pool, family: posix.sa_family_t, options: Io.net.IpAdd |
| 1871 | const socket_flags_unsupported = builtin.os.tag.isDarwin() or native_os == .haiku; // 💩💩 | 1887 | const socket_flags_unsupported = builtin.os.tag.isDarwin() or native_os == .haiku; // 💩💩 |
| 1872 | const have_accept4 = !socket_flags_unsupported; | 1888 | const have_accept4 = !socket_flags_unsupported; |
| 1873 | 1889 | ||
| 1874 | fn acceptPosix(userdata: ?*anyopaque, server: *Io.net.Server) Io.net.Server.AcceptError!Io.net.Stream { | 1890 | fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: Io.net.Socket.Handle) Io.net.Server.AcceptError!Io.net.Stream { |
| 1875 | const pool: *Pool = @ptrCast(@alignCast(userdata)); | 1891 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1876 | const listen_fd = server.socket.handle; | ||
| 1877 | var storage: PosixAddress = undefined; | 1892 | var storage: PosixAddress = undefined; |
| 1878 | var addr_len: posix.socklen_t = @sizeOf(PosixAddress); | 1893 | var addr_len: posix.socklen_t = @sizeOf(PosixAddress); |
| 1879 | const fd = while (true) { | 1894 | const fd = while (true) { |
lib/std/Io/net.zig+38-4| ... | @@ -219,7 +219,7 @@ pub const IpAddress = union(enum) { | ... | @@ -219,7 +219,7 @@ pub const IpAddress = union(enum) { |
| 219 | /// Waits for a TCP connection. When using this API, `bind` does not need | 219 | /// Waits for a TCP connection. When using this API, `bind` does not need |
| 220 | /// to be called. The returned `Server` has an open `stream`. | 220 | /// to be called. The returned `Server` has an open `stream`. |
| 221 | pub fn listen(address: IpAddress, io: Io, options: ListenOptions) ListenError!Server { | 221 | pub fn listen(address: IpAddress, io: Io, options: ListenOptions) ListenError!Server { |
| 222 | return io.vtable.listen(io.userdata, address, options); | 222 | return io.vtable.netListenIp(io.userdata, address, options); |
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | pub const BindError = error{ | 225 | pub const BindError = error{ |
| ... | @@ -262,7 +262,7 @@ pub const IpAddress = union(enum) { | ... | @@ -262,7 +262,7 @@ pub const IpAddress = union(enum) { |
| 262 | /// One bound `Socket` can be used to receive messages from multiple | 262 | /// One bound `Socket` can be used to receive messages from multiple |
| 263 | /// different addresses. | 263 | /// different addresses. |
| 264 | pub fn bind(address: *const IpAddress, io: Io, options: BindOptions) BindError!Socket { | 264 | pub fn bind(address: *const IpAddress, io: Io, options: BindOptions) BindError!Socket { |
| 265 | return io.vtable.ipBind(io.userdata, address, options); | 265 | return io.vtable.netBindIp(io.userdata, address, options); |
| 266 | } | 266 | } |
| 267 | 267 | ||
| 268 | pub const ConnectError = error{ | 268 | pub const ConnectError = error{ |
| ... | @@ -298,7 +298,7 @@ pub const IpAddress = union(enum) { | ... | @@ -298,7 +298,7 @@ pub const IpAddress = union(enum) { |
| 298 | 298 | ||
| 299 | /// Initiates a connection-oriented network stream. | 299 | /// Initiates a connection-oriented network stream. |
| 300 | pub fn connect(address: *const IpAddress, io: Io, options: ConnectOptions) ConnectError!Stream { | 300 | pub fn connect(address: *const IpAddress, io: Io, options: ConnectOptions) ConnectError!Stream { |
| 301 | return io.vtable.ipConnect(io.userdata, address, options); | 301 | return io.vtable.netConnectIp(io.userdata, address, options); |
| 302 | } | 302 | } |
| 303 | }; | 303 | }; |
| 304 | 304 | ||
| ... | @@ -775,6 +775,39 @@ pub const Ip6Address = struct { | ... | @@ -775,6 +775,39 @@ pub const Ip6Address = struct { |
| 775 | }; | 775 | }; |
| 776 | }; | 776 | }; |
| 777 | 777 | ||
| 778 | pub const UnixAddress = struct { | ||
| 779 | path: []const u8, | ||
| 780 | |||
| 781 | pub const max_len = 108; | ||
| 782 | |||
| 783 | pub const InitError = error{NameTooLong}; | ||
| 784 | |||
| 785 | pub fn init(p: []const u8) InitError!UnixAddress { | ||
| 786 | if (p.len > max_len) return error.NameTooLong; | ||
| 787 | return .{ .path = p }; | ||
| 788 | } | ||
| 789 | |||
| 790 | pub const ListenError = error{}; | ||
| 791 | |||
| 792 | pub fn listen(ua: UnixAddress, io: Io) ListenError!Server { | ||
| 793 | assert(ua.path.len <= max_len); | ||
| 794 | return .{ .socket = .{ | ||
| 795 | .handle = try io.vtable.netListenUnix(io.userdata, ua), | ||
| 796 | .address = .{ .ip4 = .loopback(0) }, | ||
| 797 | } }; | ||
| 798 | } | ||
| 799 | |||
| 800 | pub const ConnectError = error{}; | ||
| 801 | |||
| 802 | pub fn connect(ua: UnixAddress, io: Io) ConnectError!Stream { | ||
| 803 | assert(ua.path.len <= max_len); | ||
| 804 | return .{ .socket = .{ | ||
| 805 | .handle = try io.vtable.netConnectUnix(io.userdata, ua), | ||
| 806 | .address = .{ .ip4 = .loopback(0) }, | ||
| 807 | } }; | ||
| 808 | } | ||
| 809 | }; | ||
| 810 | |||
| 778 | pub const ReceiveFlags = packed struct(u8) { | 811 | pub const ReceiveFlags = packed struct(u8) { |
| 779 | oob: bool = false, | 812 | oob: bool = false, |
| 780 | peek: bool = false, | 813 | peek: bool = false, |
| ... | @@ -917,6 +950,7 @@ pub const Socket = struct { | ... | @@ -917,6 +950,7 @@ pub const Socket = struct { |
| 917 | else => std.posix.fd_t, | 950 | else => std.posix.fd_t, |
| 918 | }; | 951 | }; |
| 919 | 952 | ||
| 953 | /// Leaves `address` in a valid state. | ||
| 920 | pub fn close(s: *Socket, io: Io) void { | 954 | pub fn close(s: *Socket, io: Io) void { |
| 921 | io.vtable.netClose(io.userdata, s.handle); | 955 | io.vtable.netClose(io.userdata, s.handle); |
| 922 | s.handle = undefined; | 956 | s.handle = undefined; |
| ... | @@ -1156,7 +1190,7 @@ pub const Server = struct { | ... | @@ -1156,7 +1190,7 @@ pub const Server = struct { |
| 1156 | 1190 | ||
| 1157 | /// Blocks until a client connects to the server. | 1191 | /// Blocks until a client connects to the server. |
| 1158 | pub fn accept(s: *Server, io: Io) AcceptError!Stream { | 1192 | pub fn accept(s: *Server, io: Io) AcceptError!Stream { |
| 1159 | return io.vtable.accept(io.userdata, s); | 1193 | return io.vtable.netAccept(io.userdata, s.socket.handle); |
| 1160 | } | 1194 | } |
| 1161 | }; | 1195 | }; |
| 1162 | 1196 |
lib/std/Io/net/test.zig+5-21| ... | @@ -290,15 +290,16 @@ test "listen on a unix socket, send bytes, receive bytes" { | ... | @@ -290,15 +290,16 @@ test "listen on a unix socket, send bytes, receive bytes" { |
| 290 | const socket_path = try generateFileName("socket.unix"); | 290 | const socket_path = try generateFileName("socket.unix"); |
| 291 | defer testing.allocator.free(socket_path); | 291 | defer testing.allocator.free(socket_path); |
| 292 | 292 | ||
| 293 | const socket_addr = try net.IpAddress.initUnix(socket_path); | 293 | const socket_addr = try net.UnixAddress.init(socket_path); |
| 294 | defer std.fs.cwd().deleteFile(socket_path) catch {}; | 294 | defer std.fs.cwd().deleteFile(socket_path) catch {}; |
| 295 | 295 | ||
| 296 | var server = try socket_addr.listen(io, .{}); | 296 | var server = try socket_addr.listen(io); |
| 297 | defer server.deinit(io); | 297 | defer server.socket.close(io); |
| 298 | 298 | ||
| 299 | const S = struct { | 299 | const S = struct { |
| 300 | fn clientFn(path: []const u8) !void { | 300 | fn clientFn(path: []const u8) !void { |
| 301 | var stream = try net.connectUnixSocket(path); | 301 | const server_path: net.UnixAddress = try .init(path); |
| 302 | var stream = try server_path.connect(io); | ||
| 302 | defer stream.close(io); | 303 | defer stream.close(io); |
| 303 | 304 | ||
| 304 | var stream_writer = stream.writer(io, &.{}); | 305 | var stream_writer = stream.writer(io, &.{}); |
| ... | @@ -319,23 +320,6 @@ test "listen on a unix socket, send bytes, receive bytes" { | ... | @@ -319,23 +320,6 @@ test "listen on a unix socket, send bytes, receive bytes" { |
| 319 | try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]); | 320 | try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]); |
| 320 | } | 321 | } |
| 321 | 322 | ||
| 322 | test "listen on a unix socket with reuse_address option" { | ||
| 323 | if (!net.has_unix_sockets) return error.SkipZigTest; | ||
| 324 | // Windows doesn't implement reuse port option. | ||
| 325 | if (builtin.os.tag == .windows) return error.SkipZigTest; | ||
| 326 | |||
| 327 | const io = testing.io; | ||
| 328 | |||
| 329 | const socket_path = try generateFileName("socket.unix"); | ||
| 330 | defer testing.allocator.free(socket_path); | ||
| 331 | |||
| 332 | const socket_addr = try net.Address.initUnix(socket_path); | ||
| 333 | defer std.fs.cwd().deleteFile(socket_path) catch {}; | ||
| 334 | |||
| 335 | var server = try socket_addr.listen(io, .{ .reuse_address = true }); | ||
| 336 | server.deinit(io); | ||
| 337 | } | ||
| 338 | |||
| 339 | fn generateFileName(base_name: []const u8) ![]const u8 { | 323 | fn generateFileName(base_name: []const u8) ![]const u8 { |
| 340 | const random_bytes_count = 12; | 324 | const random_bytes_count = 12; |
| 341 | const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count); | 325 | const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count); |