╭ changed ────╮ │src/serve.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────╮ │10 const serve_http = @import("./serve_http.zig"); │ │11 │ │12 pub const Options = struct { │ │13 - http_listen: []const u8 = "127.0.0.1:8080", │ │14 - ssh_listen: []const u8 = "127.0.0.1:8022", │ │13 + http_listen: ?[]const u8 = "127.0.0.1:8080",│ │14 + ssh_listen: ?[]const u8 = "127.0.0.1:8022", │ │15 wui_listen: []const u8 = "127.0.0.1:8000", │ │16 data_dir: []const u8 = ".", │ │17 }; │ ╰────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────╮ │19 const ListenAddress = struct { │ │20 host: []const u8, │ │21 port: u16, │ │22 +}; │ │23 + │ │24 +const BoundListener = struct { │ │25 + address: ListenAddress, │ │26 + server: std.Io.net.Server, │ │27 + │ │28 + fn deinit(self: *BoundListener, io: std.Io) void {│ │29 + self.server.deinit(io); │ │30 + } │ │31 + │ │32 + fn port(self: *const BoundListener) u16 { │ │33 + return self.server.socket.address.getPort(); │ │34 + } │ │35 +}; │ │36 + │ │37 +const SshService = struct { │ │38 + listener: BoundListener, │ │39 + host_key: serve_ssh_protocol.HostKey, │ │40 + session_handler: serve_ssh.SessionHandler, │ │41 }; │ │42 │ │43 pub fn run( │ ╰──────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │76 const fallback = builtin.mode == .Debug; │ │77 │ │78 // create http listener │ │60 - │ │61 - const http_listen_address = try parseListenAddress(options.http_listen); │ │62 - var http_server = try listenWithFallback(io, http_listen_address, fallback); │ │63 - defer http_server.deinit(io); │ │64 - │ │65 - // create ssh listener │ │66 - │ │67 - const ssh_listen_address = try parseListenAddress(options.ssh_listen); │ │68 - var ssh_server = try listenWithFallback(io, ssh_listen_address, fallback); │ │69 - defer ssh_server.deinit(io); │ │79 │ │71 - // load or generate the SSH host key │ │72 - const host_key = try serve_ssh_protocol.HostKey.loadOrGenerate(io, allocator, data_dir_path); │ │80 + var http_maybe: ?BoundListener = if (options.http_listen) |value| blk: { │ │81 + const address = try parseListenAddress(value); │ │82 + break :blk .{ .address = address, .server = try listenWithFallback(io, address, fallback) };│ │83 + } else null; │ │84 + defer if (http_maybe) |*http| http.deinit(io); │ │85 │ │86 // disk-backed store for web login sessions │ │87 const session_store = try web.SessionStore.init(io, data_dir); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 92 const wui_listen_address = try parseListenAddress(options.wui_listen); │ │ 93 var wui_server = try listenWithFallback(io, wui_listen_address, fallback); │ │ 94 defer wui_server.deinit(io); │ │ 95 + │ │ 96 + // create ssh listener and its session state │ │ 97 │ │ 98 + const clone_http_port: ?u16 = if (http_maybe) |*http| http.port() else null; │ │ 99 + var ssh_maybe: ?SshService = if (options.ssh_listen) |value| blk: { │ │100 + const address = try parseListenAddress(value); │ │101 + var listener = BoundListener{ .address = address, .server = try listenWithFallback(io, address, fallback) };│ │102 + errdefer listener.deinit(io); │ │103 + const ssh_port = listener.port(); │ │104 + break :blk .{ │ │105 + .listener = listener, │ │106 + .host_key = try serve_ssh_protocol.HostKey.loadOrGenerate(io, allocator, data_dir_path), │ │107 + .session_handler = .{ │ │108 + .admin_repo_path = admin_repo_path, │ │109 + .repo_root_path = repo_root_path, │ │110 + .wui_port = wui_server.socket.address.getPort(), │ │111 + .clone_http_port = clone_http_port, │ │112 + .clone_ssh_port = ssh_port, │ │113 + .err = err, │ │114 + }, │ │115 + }; │ │116 + } else null; │ │117 + defer if (ssh_maybe) |*ssh| ssh.listener.deinit(io); │ │118 + │ │119 // start task group │ │120 │ │121 var tasks: std.Io.Group = .init; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │123 │ │124 // run listeners, printing the ports actually bound │ │125 │ │ 91 - try err.print("serving HTTP on {s}:{d}, repo root {s}\n", .{ http_listen_address.host, http_server.socket.address.getPort(), repo_root_path });│ │ 92 - try err.flush(); │ │126 + const clone_ssh_port: ?u16 = if (ssh_maybe) |*ssh| ssh.listener.port() else null; │ │127 │ │ 94 - serve_http.runListener(repo_kind, any_repo_opts, io, allocator, repo_root_path, admin_repo_path, &http_server, &tasks, err); │ │128 + if (http_maybe) |*http| { │ │129 + try err.print("serving HTTP on {s}:{d}, repo root {s}\n", .{ http.address.host, http.port(), repo_root_path }); │ │130 + try err.flush(); │ │131 + serve_http.runListener(repo_kind, any_repo_opts, io, allocator, repo_root_path, admin_repo_path, &http.server, &tasks, err); │ │132 + } │ │133 │ │ 96 - try err.print("serving SSH on {s}:{d}\n", .{ ssh_listen_address.host, ssh_server.socket.address.getPort() }); │ │ 97 - try err.flush(); │ │134 + if (ssh_maybe) |*ssh| { │ │135 + try err.print("serving SSH on {s}:{d}\n", .{ ssh.listener.address.host, ssh.listener.port() }); │ │136 + try err.flush(); │ │137 + serve_ssh.runListener(io, allocator, &ssh.host_key, &ssh.session_handler, &ssh.listener.server, &tasks, err); │ │138 + } │ │139 │ │ 99 - const ssh_session_handler = serve_ssh.SessionHandler{ │ │100 - .admin_repo_path = admin_repo_path, │ │101 - .repo_root_path = repo_root_path, │ │102 - .wui_port = wui_server.socket.address.getPort(), │ │103 - .clone_http_port = http_server.socket.address.getPort(), │ │104 - .clone_ssh_port = ssh_server.socket.address.getPort(), │ │105 - .err = err, │ │106 - }; │ │107 - serve_ssh.runListener(io, allocator, &host_key, &ssh_session_handler, &ssh_server, &tasks, err); │ │108 - │ │140 try err.print("serving web UI on http://{s}:{d}/\n", .{ wui_listen_address.host, wui_server.socket.address.getPort() }); │ │141 try err.flush(); │ │142 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │143 runWebListener(io, allocator, &wui_server, &tasks, .{ .remote = .{│ │144 .admin_repo_path = admin_repo_path, │ │145 .session_store = session_store, │ │115 - .clone_http_port = http_server.socket.address.getPort(), │ │116 - .clone_ssh_port = ssh_server.socket.address.getPort(), │ │146 + .clone_http_port = clone_http_port, │ │147 + .clone_ssh_port = clone_ssh_port, │ │148 } }, err); │ │149 │ │150 if (@TypeOf(runnable) != void) { │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │120 - try runnable.run(wui_server.socket.address.getPort(), http_server.socket.address.getPort(), ssh_server.socket.address.getPort());│ │151 + try runnable.run(wui_server.socket.address.getPort(), clone_http_port, clone_ssh_port); │ │152 } else { │ │153 try tasks.await(io); │ │154 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯