╭ serve_common.zig ─────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1 const std = @import("std"); 2 const evt = @import("./event.zig"); 3 const rp = @import("xit").repo; 4 5 // progress is optional and best effort: a failed report must not fail the work 6 pub fn reportProgress(comptime repo_opts: rp.RepoOpts(.xit), io: std.Io, progress_ctx_maybe: ?repo_opts.ProgressCtx, event: rp.ProgressEvent) void { 7 if (repo_opts.ProgressCtx != void) { 8 if (progress_ctx_maybe) |progress_ctx| progress_ctx.run(io, event) catch {}; 9 } 10 } 11 12 // the outcome of resolving a requested repo path to its on-disk directory. 13 // the http and ssh paths each map the cases to their own error responses. 14 pub const RepoPath = union(enum) { 15 ok: []const u8, // resolved on-disk path; the caller owns and frees it 16 invalid, // a <owner>/<repo> path was expected but not given 17 not_found, // unknown owner, or the repo doesn't exist and we aren't creating 18 }; 19 20 // resolve a requested repo path to its on-disk directory, parsed as 21 // <owner>/<repo> through the event store, minting a repo event for a fresh push. 22 pub fn resolveRepoPath( 23 io: std.Io, 24 allocator: std.mem.Allocator, 25 repo_root_path: []const u8, 26 admin_repo_path: []const u8, 27 requested: []const u8, 28 create_if_missing: bool, 29 ) !RepoPath { 30 const owner_repo = evt.parseOwnerRepoPath(requested) orelse return .invalid; 31 const event_id_hex = (try evt.resolveOrCreateRepo( 32 io, 33 allocator, 34 admin_repo_path, 35 owner_repo.owner, 36 owner_repo.name, 37 if (create_if_missing) .{} else null, 38 )) orelse return .not_found; 39 return .{ .ok = try std.fs.path.join(allocator, &.{ repo_root_path, &event_id_hex }) }; 40 } 41 42 // every connection task shares one error writer, so writing to it takes a 43 // lock. uncancelable, since a log line shouldn't be a cancelation point. 44 var log_mutex: std.Io.Mutex = .init; 45 46 pub fn logError(io: std.Io, err: *std.Io.Writer, comptime fmt: []const u8, args: anytype) void { 47 log_mutex.lockUncancelable(io); 48 defer log_mutex.unlock(io); 49 err.print(fmt, args) catch return; 50 err.flush() catch {}; 51 } 52 53 // accept connections forever, spawning `handleConn(context, stream)` as a task 54 // for each. the handler owns the stream (it closes it). `name` labels accept 55 // errors in the log. shared by the http, ssh, and web ui listeners. 56 pub fn runListener( 57 io: std.Io, 58 net_server: *std.Io.net.Server, 59 tasks: *std.Io.Group, 60 err: *std.Io.Writer, 61 name: []const u8, 62 context: anytype, 63 comptime handleConn: fn (@TypeOf(context), std.Io.net.Stream) void, 64 ) void { 65 const Context = @TypeOf(context); 66 67 const Conn = struct { 68 context: Context, 69 stream: std.Io.net.Stream, 70 71 fn run(c: @This()) void { 72 handleConn(c.context, c.stream); 73 } 74 }; 75 76 const Listener = struct { 77 io: std.Io, 78 net_server: *std.Io.net.Server, 79 tasks: *std.Io.Group, 80 err: *std.Io.Writer, 81 name: []const u8, 82 context: Context, 83 84 fn run(self: @This()) void { 85 while (true) { 86 const stream = self.net_server.accept(self.io) catch |accept_err| { 87 if (accept_err == error.Canceled) return; 88 logError(self.io, self.err, "{s} accept failed: {s}\n", .{ self.name, @errorName(accept_err) }); 89 continue; 90 }; 91 self.tasks.async(self.io, Conn.run, .{Conn{ .context = self.context, .stream = stream }}); 92 } 93 } 94 }; 95 96 tasks.async(io, Listener.run, .{Listener{ 97 .io = io, 98 .net_server = net_server, 99 .tasks = tasks, 100 .err = err, 101 .name = name, 102 .context = context, 103 }}); 104 } 105