╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ serve_common.zig ─────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
13 // resolve a requested repo path to its on-disk directory, parsed as 14 // <owner>/<repo> through the event store, minting a repo event for a fresh push. 15 pub fn resolveRepoPath( 16 io: std.Io, 17 allocator: std.mem.Allocator, 18 users_dir: []const u8, 19 admin_repo_path: []const u8, 20 requested: []const u8, 21 create_if_missing: bool, 22 ) !RepoPath { 23 const owner_repo = evt.parseOwnerRepoPath(requested) orelse return .invalid; 24 const location = (try evt.resolveOrCreateRepo( 25 io, 26 allocator, 27 users_dir, 28 admin_repo_path, 29 owner_repo.owner, 30 owner_repo.name, 31 if (create_if_missing) .{} else null, 32 )) orelse return .not_found; 33 return .{ .ok = try evt.repoPath(allocator, users_dir, &location.owner_id, &location.repo_id) }; 34 } 35 36 // every connection task shares one error writer, so writing to it takes a 37 // lock. uncancelable, since a log line shouldn't be a cancelation point. 38 var log_mutex: std.Io.Mutex = .init; 39 40 pub fn logError(io: std.Io, err: *std.Io.Writer, comptime fmt: []const u8, args: anytype) void { 41 log_mutex.lockUncancelable(io); 42 defer log_mutex.unlock(io); 43 err.print(fmt, args) catch return; 44 err.flush() catch {}; 45 } 46 47 // accept connections forever, spawning `handleConn(context, stream)` as a task 48 // for each. the handler owns the stream (it closes it). `name` labels accept 49 // errors in the log. shared by the http, ssh, and web ui listeners. 50 pub fn runListener( 51 io: std.Io, 52 net_server: *std.Io.net.Server, 53 tasks: *std.Io.Group, 54 err: *std.Io.Writer, 55 name: []const u8, 56 context: anytype, 57 comptime handleConn: fn (@TypeOf(context), std.Io.net.Stream) void, 58 ) void { 59 const Context = @TypeOf(context); 60 61 const Conn = struct { 62 context: Context, 63 stream: std.Io.net.Stream, 64 65 fn run(c: @This()) void { 66 handleConn(c.context, c.stream); 67 } 68 }; 69 70 const Listener = struct { 71 io: std.Io, 72 net_server: *std.Io.net.Server, 73 tasks: *std.Io.Group, 74 err: *std.Io.Writer, 75 name: []const u8, 76 context: Context, 77 78 fn run(self: @This()) void { 79 while (true) { 80 const stream = self.net_server.accept(self.io) catch |accept_err| { 81 if (accept_err == error.Canceled) return; 82 logError(self.io, self.err, "{s} accept failed: {s}\n", .{ self.name, @errorName(accept_err) }); 83 continue; 84 }; 85 self.tasks.async(self.io, Conn.run, .{Conn{ .context = self.context, .stream = stream }}); 86 } 87 } 88 }; 89 90 tasks.async(io, Listener.run, .{Listener{ 91 .io = io, 92 .net_server = net_server, 93 .tasks = tasks, 94 .err = err, 95 .name = name, 96 .context = context, 97 }}); 98 } 99