Commit 529064856a

Jon <jon@idk.club>
2023-04-24 11:23:22
std.net.StreamServer.Options: add reuse_port
1 parent 2be347a
Changed files (2)
lib
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;
 
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);