| ... | ... | @@ -1254,11 +1254,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) |
| 1254 | 1254 | |
| 1255 | 1255 | /// This API only works when `std.io.mode` is `std.io.Mode.evented`. |
| 1256 | 1256 | /// This struct is immovable after calling `listen`. |
| 1257 | | pub const Server = struct { |
| 1258 | | /// This field is meant to be accessed directly. |
| 1259 | | /// Call `connections.get` to accept a connection. |
| 1260 | | connections: *ConnectionChannel, |
| 1261 | | |
| 1257 | pub const TcpServer = struct { |
| 1262 | 1258 | /// Copied from `Options` on `init`. |
| 1263 | 1259 | kernel_backlog: u32, |
| 1264 | 1260 | |
| ... | ... | @@ -1266,63 +1262,33 @@ pub const Server = struct { |
| 1266 | 1262 | listen_address: Address, |
| 1267 | 1263 | |
| 1268 | 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; |
| 1291 | 1265 | |
| 1292 | 1266 | pub const Options = struct { |
| 1293 | 1267 | /// How many connections the kernel will accept on the application's behalf. |
| 1294 | 1268 | /// If more than this many connections pool in the kernel, clients will start |
| 1295 | 1269 | /// seeing "Connection refused". |
| 1296 | 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 | }; |
| 1302 | 1272 | |
| 1303 | 1273 | /// After this call succeeds, resources have been acquired and must |
| 1304 | 1274 | /// be released with `deinit`. |
| 1305 | | pub fn init(options: Options) !Server { |
| 1306 | | const loop = std.event.Loop.instance orelse |
| 1307 | | @compileError("std.net.Server only works in evented I/O mode"); |
| 1308 | | return Server{ |
| 1309 | | .connections = try ConnectionChannel.create(loop, options.eager_connections), |
| 1275 | pub fn init(options: Options) TcpServer { |
| 1276 | return TcpServer{ |
| 1310 | 1277 | .sockfd = null, |
| 1311 | 1278 | .kernel_backlog = options.kernel_backlog, |
| 1312 | 1279 | .listen_address = undefined, |
| 1313 | | .accept_frame = undefined, |
| 1314 | 1280 | }; |
| 1315 | 1281 | } |
| 1316 | 1282 | |
| 1317 | | /// After calling this function, one must call `init` to do anything else with this `Server`. |
| 1318 | | pub fn deinit(self: *Server) void { |
| 1283 | /// Release all resources. The `TcpServer` memory becomes `undefined`. |
| 1284 | pub fn deinit(self: *TcpServer) void { |
| 1319 | 1285 | self.close(); |
| 1320 | | self.connections.destroy(); |
| 1321 | 1286 | self.* = undefined; |
| 1322 | 1287 | } |
| 1323 | 1288 | |
| 1324 | | pub fn listen(self: *Server, address: Address) !void { |
| 1325 | | const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK; |
| 1289 | pub fn listen(self: *TcpServer, address: Address) !void { |
| 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 | 1292 | const sockfd = try os.socket(os.AF_INET, sock_flags, os.PROTO_tcp); |
| 1327 | 1293 | self.sockfd = sockfd; |
| 1328 | 1294 | errdefer { |
| ... | ... | @@ -1334,38 +1300,51 @@ pub const Server = struct { |
| 1334 | 1300 | try os.bind(sockfd, &address.os_addr, socklen); |
| 1335 | 1301 | try os.listen(sockfd, self.kernel_backlog); |
| 1336 | 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 | } |
| 1342 | 1304 | |
| 1343 | 1305 | /// Stop listening. It is still necessary to call `deinit` after stopping listening. |
| 1344 | 1306 | /// Calling `deinit` will automatically call `close`. It is safe to call `close` when |
| 1345 | 1307 | /// not listening. |
| 1346 | | pub fn close(self: *Server) void { |
| 1308 | pub fn close(self: *TcpServer) void { |
| 1347 | 1309 | if (self.sockfd) |fd| { |
| 1348 | 1310 | os.close(fd); |
| 1349 | 1311 | self.sockfd = null; |
| 1350 | | await self.accept_frame; |
| 1351 | | self.accept_frame = undefined; |
| 1352 | 1312 | self.listen_address = undefined; |
| 1353 | 1313 | } |
| 1354 | 1314 | } |
| 1355 | 1315 | |
| 1356 | | fn acceptConnections(self: *Server) void { |
| 1357 | | const sockfd = self.sockfd.?; |
| 1358 | | const accept_flags = os.SOCK_NONBLOCK | os.SOCK_CLOEXEC; |
| 1359 | | while (true) { |
| 1360 | | var accepted_addr: Address = undefined; |
| 1361 | | var addr_len: os.socklen_t = @sizeOf(os.sockaddr); |
| 1362 | | const conn = if (os.accept4(sockfd, &accepted_addr.os_addr, &addr_len, accept_flags)) |fd| |
| 1363 | | fs.File.openHandle(fd) |
| 1364 | | else |err| switch (err) { |
| 1365 | | error.WouldBlock => unreachable, // we asserted earlier about non-blocking I/O mode |
| 1366 | | else => |e| e, |
| 1367 | | }; |
| 1368 | | self.connections.put(conn); |
| 1316 | pub const AcceptError = error{ |
| 1317 | ConnectionAborted, |
| 1318 | |
| 1319 | /// The per-process limit on the number of open file descriptors has been reached. |
| 1320 | ProcessFdQuotaExceeded, |
| 1321 | |
| 1322 | /// The system-wide limit on the total number of open files has been reached. |
| 1323 | SystemFdQuotaExceeded, |
| 1324 | |
| 1325 | /// Not enough free memory. This often means that the memory allocation is limited |
| 1326 | /// by the socket buffer limits, not by the system memory. |
| 1327 | SystemResources, |
| 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 | }; |