╭ changed ────╮ │src/serve.zig│ ╰─────────────╯ ╭──────────────────────────────────────────────────╮ │14 wui_listen: []const u8 = "127.0.0.1:8000",│ │15 data_dir: []const u8 = ".", │ │16 git_ssh_prefix: []const u8 = "", │ │17 + fallback_on_address_in_use: bool = false, │ │18 }; │ │19 │ │20 const ListenAddress = struct { │ ╰──────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │73 │ │74 // create http listener │ │75 │ │76 + const port_policy: PortPolicy = if (options.fallback_on_address_in_use) .fallback else .exact;│ │77 var http_maybe: ?BoundListener = if (options.http_listen) |value| blk: { │ │78 const address = try parseListenAddress(value); │ │77 - break :blk .{ .address = address, .server = try listen(io, address, .reuse) }; │ │79 + break :blk .{ .address = address, .server = try listen(io, address, port_policy) }; │ │80 } else null; │ │81 defer if (http_maybe) |*http| http.deinit(io); │ │82 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────╮ │87 // create wui listener │ │88 │ │89 const wui_listen_address = try parseListenAddress(options.wui_listen);│ │88 - var wui_server = try listen(io, wui_listen_address, .reuse); │ │90 + var wui_server = try listen(io, wui_listen_address, port_policy); │ │91 defer wui_server.deinit(io); │ │92 │ │93 // create ssh listener and its session state │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 95 const git_http_port: ?u16 = if (http_maybe) |*http| http.port() else null; │ │ 96 var ssh_maybe: ?SshService = if (options.ssh_listen) |value| blk: { │ │ 97 const address = try parseListenAddress(value); │ │ 96 - var listener = BoundListener{ .address = address, .server = try listen(io, address, .reuse) }; │ │ 98 + var listener = BoundListener{ .address = address, .server = try listen(io, address, port_policy) };│ │ 99 errdefer listener.deinit(io); │ │100 const ssh_port = listener.port(); │ │101 break :blk .{ │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │212 │ │213 // what a listener does with a port that is already taken │ │214 const PortPolicy = enum { │ │213 - // bind it anyway, so a restart isn't blocked by the last run's connections│ │214 - // sitting in TIME_WAIT. on linux this also sets SO_REUSEPORT, so a server │ │215 - // already holding the port ends up sharing it. │ │216 - reuse, │ │215 + exact, │ │216 // leave it to whoever holds it and take an OS-assigned port instead │ │217 fallback, │ │218 }; │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │220 fn listen(io: std.Io, listen_address: ListenAddress, policy: PortPolicy) !std.Io.net.Server { │ │221 const address = try std.Io.net.IpAddress.parseIp4(listen_address.host, listen_address.port);│ │222 return switch (policy) { │ │224 - .reuse => try address.listen(io, .{ .reuse_address = true }), │ │223 + .exact => try address.listen(io, .{}), │ │224 .fallback => address.listen(io, .{}) catch |listen_err| switch (listen_err) { │ │225 error.AddressInUse => blk: { │ │226 const any_port = try std.Io.net.IpAddress.parseIp4(listen_address.host, 0); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/try.zig│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │86 ""; │ │87 defer if (git_ssh_prefix.len != 0) allocator.free(git_ssh_prefix); │ │88 serve_options.git_ssh_prefix = git_ssh_prefix; │ │89 + serve_options.fallback_on_address_in_use = true; │ │90 │ │91 const server_path = if (std.mem.eql(u8, serve_options.data_dir, ".")) │ │92 try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "server" })│ ╰────────────────────────────────────────────────────────────────────────────────────╯