authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-30 00:34:30-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-30 00:34:30-04:00
logd5e438b36e928e362032257fc77f632a2a83806a
tree8e33ac220a5663f4f7f02fe96815536f78e70403
parentc3d816a98e1126f5de4ec1a45e5f65bb2ff2f43c
signaturelock-open Commit is signed but in an unrecognized format.

rename std.net.Server to TcpServer and simplify it


2 files changed, 44 insertions(+), 65 deletions(-)

lib/std/net.zig+41-62
...@@ -1254,11 +1254,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)...@@ -1254,11 +1254,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)
12541254
1255/// This API only works when `std.io.mode` is `std.io.Mode.evented`.1255/// This API only works when `std.io.mode` is `std.io.Mode.evented`.
1256/// This struct is immovable after calling `listen`.1256/// This struct is immovable after calling `listen`.
1257pub const Server = struct {1257pub const TcpServer = struct {
1258 /// This field is meant to be accessed directly.
1259 /// Call `connections.get` to accept a connection.
1260 connections: *ConnectionChannel,
1261
1262 /// Copied from `Options` on `init`.1258 /// Copied from `Options` on `init`.
1263 kernel_backlog: u32,1259 kernel_backlog: u32,
12641260
...@@ -1266,63 +1262,33 @@ pub const Server = struct {...@@ -1266,63 +1262,33 @@ pub const Server = struct {
1266 listen_address: Address,1262 listen_address: Address,
12671263
1268 sockfd: ?os.fd_t,1264 sockfd: ?os.fd_t,
1269 accept_frame: @Frame(acceptConnections),
1270
1271 pub const ConnectionChannel = std.event.Channel(AcceptError!fs.File);
1272
1273 pub const AcceptError = error{
1274 ConnectionAborted,
1275
1276 /// The per-process limit on the number of open file descriptors has been reached.
1277 ProcessFdQuotaExceeded,
1278
1279 /// The system-wide limit on the total number of open files has been reached.
1280 SystemFdQuotaExceeded,
1281
1282 /// Not enough free memory. This often means that the memory allocation is limited
1283 /// by the socket buffer limits, not by the system memory.
1284 SystemResources,
1285
1286 ProtocolFailure,
1287
1288 /// Firewall rules forbid connection.
1289 BlockedByFirewall,
1290 } || os.UnexpectedError;
12911265
1292 pub const Options = struct {1266 pub const Options = struct {
1293 /// How many connections the kernel will accept on the application's behalf.1267 /// How many connections the kernel will accept on the application's behalf.
1294 /// If more than this many connections pool in the kernel, clients will start1268 /// If more than this many connections pool in the kernel, clients will start
1295 /// seeing "Connection refused".1269 /// seeing "Connection refused".
1296 kernel_backlog: u32 = 128,1270 kernel_backlog: u32 = 128,
1297
1298 /// How many connections this `Server` will accept from the kernel even before
1299 /// they are requested from the `connections` channel.
1300 eager_connections: usize = 16,
1301 };1271 };
13021272
1303 /// After this call succeeds, resources have been acquired and must1273 /// After this call succeeds, resources have been acquired and must
1304 /// be released with `deinit`.1274 /// be released with `deinit`.
1305 pub fn init(options: Options) !Server {1275 pub fn init(options: Options) TcpServer {
1306 const loop = std.event.Loop.instance orelse1276 return TcpServer{
1307 @compileError("std.net.Server only works in evented I/O mode");
1308 return Server{
1309 .connections = try ConnectionChannel.create(loop, options.eager_connections),
1310 .sockfd = null,1277 .sockfd = null,
1311 .kernel_backlog = options.kernel_backlog,1278 .kernel_backlog = options.kernel_backlog,
1312 .listen_address = undefined,1279 .listen_address = undefined,
1313 .accept_frame = undefined,
1314 };1280 };
1315 }1281 }
13161282
1317 /// After calling this function, one must call `init` to do anything else with this `Server`.1283 /// Release all resources. The `TcpServer` memory becomes `undefined`.
1318 pub fn deinit(self: *Server) void {1284 pub fn deinit(self: *TcpServer) void {
1319 self.close();1285 self.close();
1320 self.connections.destroy();
1321 self.* = undefined;1286 self.* = undefined;
1322 }1287 }
13231288
1324 pub fn listen(self: *Server, address: Address) !void {1289 pub fn listen(self: *TcpServer, address: Address) !void {
1325 const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK;1290 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
1291 const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock;
1326 const sockfd = try os.socket(os.AF_INET, sock_flags, os.PROTO_tcp);1292 const sockfd = try os.socket(os.AF_INET, sock_flags, os.PROTO_tcp);
1327 self.sockfd = sockfd;1293 self.sockfd = sockfd;
1328 errdefer {1294 errdefer {
...@@ -1334,38 +1300,51 @@ pub const Server = struct {...@@ -1334,38 +1300,51 @@ pub const Server = struct {
1334 try os.bind(sockfd, &address.os_addr, socklen);1300 try os.bind(sockfd, &address.os_addr, socklen);
1335 try os.listen(sockfd, self.kernel_backlog);1301 try os.listen(sockfd, self.kernel_backlog);
1336 try os.getsockname(sockfd, &self.listen_address.os_addr, &socklen);1302 try os.getsockname(sockfd, &self.listen_address.os_addr, &socklen);
1337
1338 // acceptConnections loops, calling os.accept().
1339 self.accept_frame = async self.acceptConnections();
1340 errdefer await self.accept_frame;
1341 }1303 }
13421304
1343 /// Stop listening. It is still necessary to call `deinit` after stopping listening.1305 /// Stop listening. It is still necessary to call `deinit` after stopping listening.
1344 /// Calling `deinit` will automatically call `close`. It is safe to call `close` when1306 /// Calling `deinit` will automatically call `close`. It is safe to call `close` when
1345 /// not listening.1307 /// not listening.
1346 pub fn close(self: *Server) void {1308 pub fn close(self: *TcpServer) void {
1347 if (self.sockfd) |fd| {1309 if (self.sockfd) |fd| {
1348 os.close(fd);1310 os.close(fd);
1349 self.sockfd = null;1311 self.sockfd = null;
1350 await self.accept_frame;
1351 self.accept_frame = undefined;
1352 self.listen_address = undefined;1312 self.listen_address = undefined;
1353 }1313 }
1354 }1314 }
13551315
1356 fn acceptConnections(self: *Server) void {1316 pub const AcceptError = error{
1357 const sockfd = self.sockfd.?;1317 ConnectionAborted,
1358 const accept_flags = os.SOCK_NONBLOCK | os.SOCK_CLOEXEC;1318
1359 while (true) {1319 /// The per-process limit on the number of open file descriptors has been reached.
1360 var accepted_addr: Address = undefined;1320 ProcessFdQuotaExceeded,
1361 var addr_len: os.socklen_t = @sizeOf(os.sockaddr);1321
1362 const conn = if (os.accept4(sockfd, &accepted_addr.os_addr, &addr_len, accept_flags)) |fd|1322 /// The system-wide limit on the total number of open files has been reached.
1363 fs.File.openHandle(fd)1323 SystemFdQuotaExceeded,
1364 else |err| switch (err) {1324
1365 error.WouldBlock => unreachable, // we asserted earlier about non-blocking I/O mode1325 /// Not enough free memory. This often means that the memory allocation is limited
1366 else => |e| e,1326 /// by the socket buffer limits, not by the system memory.
1367 };1327 SystemResources,
1368 self.connections.put(conn);1328
1329 ProtocolFailure,
1330
1331 /// Firewall rules forbid connection.
1332 BlockedByFirewall,
1333 } || os.UnexpectedError;
1334
1335 /// If this function succeeds, the returned `fs.File` is a caller-managed resource.
1336 pub fn accept(self: *TcpServer) AcceptError!fs.File {
1337 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
1338 const accept_flags = nonblock | os.SOCK_CLOEXEC;
1339 var accepted_addr: Address = undefined;
1340 var adr_len: os.socklen_t = @sizeOf(os.sockaddr);
1341 if (os.accept4(self.sockfd.?, &accepted_addr.os_addr, &adr_len, accept_flags)) |fd| {
1342 return fs.File.openHandle(fd);
1343 } else |err| switch (err) {
1344 // We only give SOCK_NONBLOCK when I/O mode is async, in which case this error
1345 // is handled by os.accept4.
1346 error.WouldBlock => unreachable,
1347 else => |e| return e,
1369 }1348 }
1370 }1349 }
1371};1350};
lib/std/net/test.zig+3-3
...@@ -42,7 +42,7 @@ test "listen on a port, send bytes, receive bytes" {...@@ -42,7 +42,7 @@ test "listen on a port, send bytes, receive bytes" {
42 // TODO doing this at comptime crashed the compiler42 // TODO doing this at comptime crashed the compiler
43 const localhost = net.Address.initIp4(net.parseIp4("127.0.0.1") catch unreachable, 0);43 const localhost = net.Address.initIp4(net.parseIp4("127.0.0.1") catch unreachable, 0);
4444
45 var server = try net.Server.init(net.Server.Options{});45 var server = net.TcpServer.init(net.TcpServer.Options{});
46 defer server.deinit();46 defer server.deinit();
47 try server.listen(localhost);47 try server.listen(localhost);
4848
...@@ -63,8 +63,8 @@ fn testClient(addr: net.Address) anyerror!void {...@@ -63,8 +63,8 @@ fn testClient(addr: net.Address) anyerror!void {
63 testing.expect(mem.eql(u8, msg, "hello from server\n"));63 testing.expect(mem.eql(u8, msg, "hello from server\n"));
64}64}
6565
66fn testServer(server: *net.Server) anyerror!void {66fn testServer(server: *net.TcpServer) anyerror!void {
67 var client_file = try server.connections.get();67 var client_file = try server.accept();
6868
69 const stream = &client_file.outStream().stream;69 const stream = &client_file.outStream().stream;
70 try stream.print("hello from server\n");70 try stream.print("hello from server\n");