Commit d5e438b36e

Andrew Kelley <andrew@ziglang.org>
2019-10-30 05:34:30
rename std.net.Server to TcpServer and simplify it
1 parent c3d816a
Changed files (2)
lib
lib/std/net/test.zig
@@ -42,7 +42,7 @@ test "listen on a port, send bytes, receive bytes" {
     // TODO doing this at comptime crashed the compiler
     const localhost = net.Address.initIp4(net.parseIp4("127.0.0.1") catch unreachable, 0);
 
-    var server = try net.Server.init(net.Server.Options{});
+    var server = net.TcpServer.init(net.TcpServer.Options{});
     defer server.deinit();
     try server.listen(localhost);
 
@@ -63,8 +63,8 @@ fn testClient(addr: net.Address) anyerror!void {
     testing.expect(mem.eql(u8, msg, "hello from server\n"));
 }
 
-fn testServer(server: *net.Server) anyerror!void {
-    var client_file = try server.connections.get();
+fn testServer(server: *net.TcpServer) anyerror!void {
+    var client_file = try server.accept();
 
     const stream = &client_file.outStream().stream;
     try stream.print("hello from server\n");
lib/std/net.zig
@@ -1254,11 +1254,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)
 
 /// This API only works when `std.io.mode` is `std.io.Mode.evented`.
 /// This struct is immovable after calling `listen`.
-pub const Server = struct {
-    /// This field is meant to be accessed directly.
-    /// Call `connections.get` to accept a connection.
-    connections: *ConnectionChannel,
-
+pub const TcpServer = struct {
     /// Copied from `Options` on `init`.
     kernel_backlog: u32,
 
@@ -1266,63 +1262,33 @@ pub const Server = struct {
     listen_address: Address,
 
     sockfd: ?os.fd_t,
-    accept_frame: @Frame(acceptConnections),
-
-    pub const ConnectionChannel = std.event.Channel(AcceptError!fs.File);
-
-    pub const AcceptError = error{
-        ConnectionAborted,
-
-        /// The per-process limit on the number of open file descriptors has been reached.
-        ProcessFdQuotaExceeded,
-
-        /// The system-wide limit on the total number of open files has been reached.
-        SystemFdQuotaExceeded,
-
-        /// Not enough free memory.  This often means that the memory allocation  is  limited
-        /// by the socket buffer limits, not by the system memory.
-        SystemResources,
-
-        ProtocolFailure,
-
-        /// Firewall rules forbid connection.
-        BlockedByFirewall,
-    } || os.UnexpectedError;
 
     pub const Options = struct {
         /// How many connections the kernel will accept on the application's behalf.
         /// If more than this many connections pool in the kernel, clients will start
         /// seeing "Connection refused".
         kernel_backlog: u32 = 128,
-
-        /// How many connections this `Server` will accept from the kernel even before
-        /// they are requested from the `connections` channel.
-        eager_connections: usize = 16,
     };
 
     /// After this call succeeds, resources have been acquired and must
     /// be released with `deinit`.
-    pub fn init(options: Options) !Server {
-        const loop = std.event.Loop.instance orelse
-            @compileError("std.net.Server only works in evented I/O mode");
-        return Server{
-            .connections = try ConnectionChannel.create(loop, options.eager_connections),
+    pub fn init(options: Options) TcpServer {
+        return TcpServer{
             .sockfd = null,
             .kernel_backlog = options.kernel_backlog,
             .listen_address = undefined,
-            .accept_frame = undefined,
         };
     }
 
-    /// After calling this function, one must call `init` to do anything else with this `Server`.
-    pub fn deinit(self: *Server) void {
+    /// Release all resources. The `TcpServer` memory becomes `undefined`.
+    pub fn deinit(self: *TcpServer) void {
         self.close();
-        self.connections.destroy();
         self.* = undefined;
     }
 
-    pub fn listen(self: *Server, address: Address) !void {
-        const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK;
+    pub fn listen(self: *TcpServer, address: Address) !void {
+        const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
+        const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock;
         const sockfd = try os.socket(os.AF_INET, sock_flags, os.PROTO_tcp);
         self.sockfd = sockfd;
         errdefer {
@@ -1334,38 +1300,51 @@ pub const Server = struct {
         try os.bind(sockfd, &address.os_addr, socklen);
         try os.listen(sockfd, self.kernel_backlog);
         try os.getsockname(sockfd, &self.listen_address.os_addr, &socklen);
-
-        // acceptConnections loops, calling os.accept().
-        self.accept_frame = async self.acceptConnections();
-        errdefer await self.accept_frame;
     }
 
     /// Stop listening. It is still necessary to call `deinit` after stopping listening.
     /// Calling `deinit` will automatically call `close`. It is safe to call `close` when
     /// not listening.
-    pub fn close(self: *Server) void {
+    pub fn close(self: *TcpServer) void {
         if (self.sockfd) |fd| {
             os.close(fd);
             self.sockfd = null;
-            await self.accept_frame;
-            self.accept_frame = undefined;
             self.listen_address = undefined;
         }
     }
 
-    fn acceptConnections(self: *Server) void {
-        const sockfd = self.sockfd.?;
-        const accept_flags = os.SOCK_NONBLOCK | os.SOCK_CLOEXEC;
-        while (true) {
-            var accepted_addr: Address = undefined;
-            var addr_len: os.socklen_t = @sizeOf(os.sockaddr);
-            const conn = if (os.accept4(sockfd, &accepted_addr.os_addr, &addr_len, accept_flags)) |fd|
-                fs.File.openHandle(fd)
-            else |err| switch (err) {
-                error.WouldBlock => unreachable, // we asserted earlier about non-blocking I/O mode
-                else => |e| e,
-            };
-            self.connections.put(conn);
+    pub const AcceptError = error{
+        ConnectionAborted,
+
+        /// The per-process limit on the number of open file descriptors has been reached.
+        ProcessFdQuotaExceeded,
+
+        /// The system-wide limit on the total number of open files has been reached.
+        SystemFdQuotaExceeded,
+
+        /// Not enough free memory.  This often means that the memory allocation  is  limited
+        /// by the socket buffer limits, not by the system memory.
+        SystemResources,
+
+        ProtocolFailure,
+
+        /// Firewall rules forbid connection.
+        BlockedByFirewall,
+    } || os.UnexpectedError;
+
+    /// If this function succeeds, the returned `fs.File` is a caller-managed resource.
+    pub fn accept(self: *TcpServer) AcceptError!fs.File {
+        const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
+        const accept_flags = nonblock | os.SOCK_CLOEXEC;
+        var accepted_addr: Address = undefined;
+        var adr_len: os.socklen_t = @sizeOf(os.sockaddr);
+        if (os.accept4(self.sockfd.?, &accepted_addr.os_addr, &adr_len, accept_flags)) |fd| {
+            return fs.File.openHandle(fd);
+        } else |err| switch (err) {
+            // We only give SOCK_NONBLOCK when I/O mode is async, in which case this error
+            // is handled by os.accept4.
+            error.WouldBlock => unreachable,
+            else => |e| return e,
         }
     }
 };