From 529064856a2e5466c890869f786b73442bc5c84b Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 24 Apr 2023 05:23:22 -0400 Subject: [PATCH] std.net.StreamServer.Options: add reuse_port --- lib/std/net.zig | 13 +++++++++++++ lib/std/net/test.zig | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/lib/std/net.zig b/lib/std/net.zig index 8800bbe6be..205fbf4842 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1867,6 +1867,7 @@ pub const StreamServer = struct { /// Copied from `Options` on `init`. kernel_backlog: u31, reuse_address: bool, + reuse_port: bool, /// `undefined` until `listen` returns successfully. listen_address: Address, @@ -1881,6 +1882,9 @@ pub const StreamServer = struct { /// Enable SO.REUSEADDR on the socket. reuse_address: bool = false, + + /// Enable SO.REUSEPORT on the socket. + reuse_port: bool = false, }; /// After this call succeeds, resources have been acquired and must @@ -1890,6 +1894,7 @@ pub const StreamServer = struct { .sockfd = null, .kernel_backlog = options.kernel_backlog, .reuse_address = options.reuse_address, + .reuse_port = options.reuse_port, .listen_address = undefined, }; } @@ -1920,6 +1925,14 @@ pub const StreamServer = struct { &mem.toBytes(@as(c_int, 1)), ); } + if (@hasDecl(os.SO, "REUSEPORT") and self.reuse_port) { + try os.setsockopt( + sockfd, + os.SOL.SOCKET, + os.SO.REUSEPORT, + &mem.toBytes(@as(c_int, 1)), + ); + } var socklen = address.getOsSockLen(); try os.bind(sockfd, &address.any, socklen); diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index c4afc0e4e3..f59d73d621 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -230,6 +230,27 @@ test "listen on ipv4 try connect on ipv6 then ipv4" { try await client_frame; } +test "listen on an in use port" { + if (builtin.os.tag != .linux and comptime !builtin.os.tag.isDarwin()) { + // TODO build abstractions for other operating systems + return error.SkipZigTest; + } + + const localhost = try net.Address.parseIp("127.0.0.1", 0); + + var server1 = net.StreamServer.init(net.StreamServer.Options{ + .reuse_port = true, + }); + defer server1.deinit(); + try server1.listen(localhost); + + var server2 = net.StreamServer.init(net.StreamServer.Options{ + .reuse_port = true, + }); + defer server2.deinit(); + try server2.listen(server1.listen_address); +} + fn testClientToHost(allocator: mem.Allocator, name: []const u8, port: u16) anyerror!void { if (builtin.os.tag == .wasi) return error.SkipZigTest;