| author | |
| committer | |
| log | e8cea8accb7f289fd00ee82063df32882748ac48 |
| tree | e4cb30b59d3e4004a3113ea794c890ee9dc8afd5 |
| parent | d680b9e9b2320612a4e988763f5090c4ae8f9a8f |
5 files changed, 130 insertions(+), 17 deletions(-)
BRANCH_TODO+1| ... | ... | @@ -10,5 +10,6 @@ |
| 10 | 10 | * address the cancelation race condition (signal received between checkCancel and syscall) |
| 11 | 11 | * update signal values to be an enum |
| 12 | 12 | * move fs.File.Writer to Io |
| 13 | * add non-blocking flag to network operations, handle EAGAIN | |
| 13 | 14 | * finish moving std.fs to Io |
| 14 | 15 | * finish moving all of std.posix into Threaded |
lib/std/Io.zig+3-3| ... | ... | @@ -672,12 +672,12 @@ pub const VTable = struct { |
| 672 | 672 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, |
| 673 | 673 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, |
| 674 | 674 | |
| 675 | netListenIp: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server, | |
| 675 | netListenIp: *const fn (?*anyopaque, address: net.IpAddress, net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server, | |
| 676 | 676 | netAccept: *const fn (?*anyopaque, server: net.Socket.Handle) net.Server.AcceptError!net.Stream, |
| 677 | 677 | netBindIp: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket, |
| 678 | 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 | netListenUnix: *const fn (?*anyopaque, *const net.UnixAddress, net.UnixAddress.ListenOptions) net.UnixAddress.ListenError!net.Socket.Handle, | |
| 680 | netConnectUnix: *const fn (?*anyopaque, *const net.UnixAddress) net.UnixAddress.ConnectError!net.Socket.Handle, | |
| 681 | 681 | netSend: *const fn (?*anyopaque, net.Socket.Handle, []net.OutgoingMessage, net.SendFlags) struct { ?net.Socket.SendError, usize }, |
| 682 | 682 | netReceive: *const fn (?*anyopaque, net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, net.ReceiveFlags, Timeout) struct { ?net.Socket.ReceiveTimeoutError, usize }, |
| 683 | 683 | /// Returns 0 on end of stream. |
lib/std/Io/Threaded.zig+91-7| ... | ... | @@ -1756,11 +1756,80 @@ fn netListenIpPosix( |
| 1756 | 1756 | }; |
| 1757 | 1757 | } |
| 1758 | 1758 | |
| 1759 | fn netListenUnix(userdata: ?*anyopaque, address: Io.net.UnixAddress) Io.net.UnixAddress.ListenError!Io.net.Socket.Handle { | |
| 1759 | fn netListenUnix( | |
| 1760 | userdata: ?*anyopaque, | |
| 1761 | address: *const Io.net.UnixAddress, | |
| 1762 | options: Io.net.UnixAddress.ListenOptions, | |
| 1763 | ) Io.net.UnixAddress.ListenError!Io.net.Socket.Handle { | |
| 1764 | if (!Io.net.has_unix_sockets) return error.AddressFamilyUnsupported; | |
| 1760 | 1765 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1761 | _ = pool; | |
| 1762 | _ = address; | |
| 1763 | @panic("TODO"); | |
| 1766 | const protocol: u32 = 0; | |
| 1767 | const socket_fd = while (true) { | |
| 1768 | try pool.checkCancel(); | |
| 1769 | const flags: u32 = posix.SOCK.STREAM | if (socket_flags_unsupported) 0 else posix.SOCK.CLOEXEC; | |
| 1770 | const socket_rc = posix.system.socket(posix.AF.UNIX, flags, protocol); | |
| 1771 | switch (posix.errno(socket_rc)) { | |
| 1772 | .SUCCESS => { | |
| 1773 | const fd: posix.fd_t = @intCast(socket_rc); | |
| 1774 | errdefer posix.close(fd); | |
| 1775 | if (socket_flags_unsupported) while (true) { | |
| 1776 | try pool.checkCancel(); | |
| 1777 | switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) { | |
| 1778 | .SUCCESS => break, | |
| 1779 | .INTR => continue, | |
| 1780 | else => |err| return posix.unexpectedErrno(err), | |
| 1781 | } | |
| 1782 | }; | |
| 1783 | break fd; | |
| 1784 | }, | |
| 1785 | .INTR => continue, | |
| 1786 | .AFNOSUPPORT => return error.AddressFamilyUnsupported, | |
| 1787 | .MFILE => return error.ProcessFdQuotaExceeded, | |
| 1788 | .NFILE => return error.SystemFdQuotaExceeded, | |
| 1789 | .NOBUFS => return error.SystemResources, | |
| 1790 | .NOMEM => return error.SystemResources, | |
| 1791 | else => |err| return posix.unexpectedErrno(err), | |
| 1792 | } | |
| 1793 | }; | |
| 1794 | errdefer posix.close(socket_fd); | |
| 1795 | ||
| 1796 | var storage: UnixAddress = undefined; | |
| 1797 | const addr_len = addressUnixToPosix(address, &storage); | |
| 1798 | while (true) { | |
| 1799 | try pool.checkCancel(); | |
| 1800 | switch (posix.errno(posix.system.bind(socket_fd, &storage.any, addr_len))) { | |
| 1801 | .SUCCESS => break, | |
| 1802 | .INTR => continue, | |
| 1803 | .ACCES => return error.AccessDenied, | |
| 1804 | .PERM => return error.PermissionDenied, | |
| 1805 | .ADDRINUSE => return error.AddressInUse, | |
| 1806 | .AFNOSUPPORT => return error.AddressFamilyUnsupported, | |
| 1807 | .ADDRNOTAVAIL => return error.AddressUnavailable, | |
| 1808 | .NOMEM => return error.SystemResources, | |
| 1809 | .LOOP => return error.SymLinkLoop, | |
| 1810 | .NOENT => return error.FileNotFound, | |
| 1811 | .NOTDIR => return error.NotDir, | |
| 1812 | .ROFS => return error.ReadOnlyFileSystem, | |
| 1813 | .BADF => |err| return errnoBug(err), // always a race condition if this error is returned | |
| 1814 | .INVAL => |err| return errnoBug(err), // invalid parameters | |
| 1815 | .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd` | |
| 1816 | .FAULT => |err| return errnoBug(err), // invalid `addr` pointer | |
| 1817 | .NAMETOOLONG => |err| return errnoBug(err), | |
| 1818 | else => |err| return posix.unexpectedErrno(err), | |
| 1819 | } | |
| 1820 | } | |
| 1821 | ||
| 1822 | while (true) { | |
| 1823 | try pool.checkCancel(); | |
| 1824 | switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) { | |
| 1825 | .SUCCESS => break, | |
| 1826 | .ADDRINUSE => return error.AddressInUse, | |
| 1827 | .BADF => |err| return errnoBug(err), | |
| 1828 | else => |err| return posix.unexpectedErrno(err), | |
| 1829 | } | |
| 1830 | } | |
| 1831 | ||
| 1832 | return socket_fd; | |
| 1764 | 1833 | } |
| 1765 | 1834 | |
| 1766 | 1835 | fn posixBind(pool: *Pool, socket_fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void { |
| ... | ... | @@ -1791,7 +1860,7 @@ fn posixConnect(pool: *Pool, socket_fd: posix.socket_t, addr: *const posix.socka |
| 1791 | 1860 | .ADDRINUSE => return error.AddressInUse, |
| 1792 | 1861 | .ADDRNOTAVAIL => return error.AddressUnavailable, |
| 1793 | 1862 | .AFNOSUPPORT => return error.AddressFamilyUnsupported, |
| 1794 | .AGAIN, .INPROGRESS => |err| return errnoBug(err), | |
| 1863 | .AGAIN, .INPROGRESS => return error.WouldBlock, | |
| 1795 | 1864 | .ALREADY => return error.ConnectionPending, |
| 1796 | 1865 | .BADF => |err| return errnoBug(err), |
| 1797 | 1866 | .CONNREFUSED => return error.ConnectionRefused, |
| ... | ... | @@ -1804,8 +1873,8 @@ fn posixConnect(pool: *Pool, socket_fd: posix.socket_t, addr: *const posix.socka |
| 1804 | 1873 | .PROTOTYPE => |err| return errnoBug(err), |
| 1805 | 1874 | .TIMEDOUT => return error.ConnectionTimedOut, |
| 1806 | 1875 | .CONNABORTED => |err| return errnoBug(err), |
| 1876 | .ACCES => return error.AccessDenied, | |
| 1807 | 1877 | // UNIX socket error codes: |
| 1808 | .ACCES => |err| return errnoBug(err), | |
| 1809 | 1878 | .PERM => |err| return errnoBug(err), |
| 1810 | 1879 | .NOENT => |err| return errnoBug(err), |
| 1811 | 1880 | else => |err| return posix.unexpectedErrno(err), |
| ... | ... | @@ -1867,7 +1936,10 @@ fn netConnectIpPosix( |
| 1867 | 1936 | } }; |
| 1868 | 1937 | } |
| 1869 | 1938 | |
| 1870 | fn netConnectUnix(userdata: ?*anyopaque, address: Io.net.UnixAddress) Io.net.UnixAddress.ConnectError!Io.net.Socket.Handle { | |
| 1939 | fn netConnectUnix( | |
| 1940 | userdata: ?*anyopaque, | |
| 1941 | address: *const Io.net.UnixAddress, | |
| 1942 | ) Io.net.UnixAddress.ConnectError!Io.net.Socket.Handle { | |
| 1871 | 1943 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1872 | 1944 | _ = pool; |
| 1873 | 1945 | _ = address; |
| ... | ... | @@ -2503,6 +2575,11 @@ const PosixAddress = extern union { |
| 2503 | 2575 | in6: posix.sockaddr.in6, |
| 2504 | 2576 | }; |
| 2505 | 2577 | |
| 2578 | const UnixAddress = extern union { | |
| 2579 | any: posix.sockaddr, | |
| 2580 | un: posix.sockaddr.un, | |
| 2581 | }; | |
| 2582 | ||
| 2506 | 2583 | fn posixAddressFamily(a: *const Io.net.IpAddress) posix.sa_family_t { |
| 2507 | 2584 | return switch (a.*) { |
| 2508 | 2585 | .ip4 => posix.AF.INET, |
| ... | ... | @@ -2531,6 +2608,13 @@ fn addressToPosix(a: *const Io.net.IpAddress, storage: *PosixAddress) posix.sock |
| 2531 | 2608 | }; |
| 2532 | 2609 | } |
| 2533 | 2610 | |
| 2611 | fn addressUnixToPosix(a: *const Io.net.UnixAddress, storage: *UnixAddress) posix.socklen_t { | |
| 2612 | @memcpy(storage.un.path[0..a.path.len], a.path); | |
| 2613 | storage.un.family = posix.AF.UNIX; | |
| 2614 | storage.un.path[a.path.len] = 0; | |
| 2615 | return @sizeOf(posix.sockaddr.un); | |
| 2616 | } | |
| 2617 | ||
| 2534 | 2618 | fn address4FromPosix(in: *posix.sockaddr.in) Io.net.Ip4Address { |
| 2535 | 2619 | return .{ |
| 2536 | 2620 | .port = std.mem.bigToNative(u16, in.port), |
lib/std/Io/net.zig+34-6| ... | ... | @@ -51,6 +51,8 @@ pub const has_unix_sockets = switch (native_os) { |
| 51 | 51 | else => true, |
| 52 | 52 | }; |
| 53 | 53 | |
| 54 | pub const default_kernel_backlog = 128; | |
| 55 | ||
| 54 | 56 | pub const IpAddress = union(enum) { |
| 55 | 57 | ip4: Ip4Address, |
| 56 | 58 | ip6: Ip6Address, |
| ... | ... | @@ -210,7 +212,7 @@ pub const IpAddress = union(enum) { |
| 210 | 212 | /// How many connections the kernel will accept on the application's behalf. |
| 211 | 213 | /// If more than this many connections pool in the kernel, clients will start |
| 212 | 214 | /// seeing "Connection refused". |
| 213 | kernel_backlog: u31 = 128, | |
| 215 | kernel_backlog: u31 = default_kernel_backlog, | |
| 214 | 216 | /// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX. |
| 215 | 217 | /// Sets SO_REUSEADDR on Windows, which is roughly equivalent. |
| 216 | 218 | reuse_address: bool = false, |
| ... | ... | @@ -288,6 +290,11 @@ pub const IpAddress = union(enum) { |
| 288 | 290 | ProtocolUnsupportedBySystem, |
| 289 | 291 | ProtocolUnsupportedByAddressFamily, |
| 290 | 292 | SocketModeUnsupported, |
| 293 | /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or | |
| 294 | /// the connection request failed because of a local firewall rule. | |
| 295 | AccessDenied, | |
| 296 | /// Non-blocking was requested and the operation cannot return immediately. | |
| 297 | WouldBlock, | |
| 291 | 298 | } || Io.Timeout.Error || Io.UnexpectedError || Io.Cancelable; |
| 292 | 299 | |
| 293 | 300 | pub const ConnectOptions = struct { |
| ... | ... | @@ -804,19 +811,40 @@ pub const UnixAddress = struct { |
| 804 | 811 | return .{ .path = p }; |
| 805 | 812 | } |
| 806 | 813 | |
| 807 | pub const ListenError = error{}; | |
| 814 | pub const ListenError = error{ | |
| 815 | AddressFamilyUnsupported, | |
| 816 | AddressInUse, | |
| 817 | NetworkDown, | |
| 818 | SystemResources, | |
| 819 | SymLinkLoop, | |
| 820 | FileNotFound, | |
| 821 | NotDir, | |
| 822 | ReadOnlyFileSystem, | |
| 823 | ProcessFdQuotaExceeded, | |
| 824 | SystemFdQuotaExceeded, | |
| 825 | AccessDenied, | |
| 826 | PermissionDenied, | |
| 827 | AddressUnavailable, | |
| 828 | } || Io.Cancelable || Io.UnexpectedError; | |
| 829 | ||
| 830 | pub const ListenOptions = struct { | |
| 831 | /// How many connections the kernel will accept on the application's behalf. | |
| 832 | /// If more than this many connections pool in the kernel, clients will start | |
| 833 | /// seeing "Connection refused". | |
| 834 | kernel_backlog: u31 = default_kernel_backlog, | |
| 835 | }; | |
| 808 | 836 | |
| 809 | pub fn listen(ua: UnixAddress, io: Io) ListenError!Server { | |
| 837 | pub fn listen(ua: *const UnixAddress, io: Io, options: ListenOptions) ListenError!Server { | |
| 810 | 838 | assert(ua.path.len <= max_len); |
| 811 | 839 | return .{ .socket = .{ |
| 812 | .handle = try io.vtable.netListenUnix(io.userdata, ua), | |
| 840 | .handle = try io.vtable.netListenUnix(io.userdata, ua, options), | |
| 813 | 841 | .address = .{ .ip4 = .loopback(0) }, |
| 814 | 842 | } }; |
| 815 | 843 | } |
| 816 | 844 | |
| 817 | pub const ConnectError = error{}; | |
| 845 | pub const ConnectError = error{} || Io.Cancelable || Io.UnexpectedError; | |
| 818 | 846 | |
| 819 | pub fn connect(ua: UnixAddress, io: Io) ConnectError!Stream { | |
| 847 | pub fn connect(ua: *const UnixAddress, io: Io) ConnectError!Stream { | |
| 820 | 848 | assert(ua.path.len <= max_len); |
| 821 | 849 | return .{ .socket = .{ |
| 822 | 850 | .handle = try io.vtable.netConnectUnix(io.userdata, ua), |
lib/std/Io/net/test.zig+1-1| ... | ... | @@ -273,7 +273,7 @@ test "listen on a unix socket, send bytes, receive bytes" { |
| 273 | 273 | const socket_addr = try net.UnixAddress.init(socket_path); |
| 274 | 274 | defer std.fs.cwd().deleteFile(socket_path) catch {}; |
| 275 | 275 | |
| 276 | var server = try socket_addr.listen(io); | |
| 276 | var server = try socket_addr.listen(io, .{}); | |
| 277 | 277 | defer server.socket.close(io); |
| 278 | 278 | |
| 279 | 279 | const S = struct { |