╭ changed ─────────╮ │src/serve_http.zig│ ╰──────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 4 const evt = @import("./event.zig"); │ │ 5 const serve_common = @import("./serve_common.zig"); │ │ 6 │ │ 7 -pub fn runListener( │ │ 8 - comptime repo_kind: rp.RepoKind, │ │ 9 - comptime any_repo_opts: rp.AnyRepoOpts(repo_kind), │ │10 - io: std.Io, │ │11 - allocator: std.mem.Allocator, │ │12 - users_dir: []const u8, │ │13 - admin_repo_path: []const u8, │ │14 - net_server: *std.Io.net.Server, │ │15 - tasks: *std.Io.Group, │ │16 - err: *std.Io.Writer, │ │17 -) void { │ │18 - const Context = struct { │ │19 - io: std.Io, │ │20 - allocator: std.mem.Allocator, │ │21 - users_dir: []const u8, │ │22 - admin_repo_path: []const u8, │ │23 - err: *std.Io.Writer, │ │ 7 +// whether a web listener request is git's smart http rather than a page │ │ 8 +pub fn isGitRequest(head: *const std.http.Server.Request.Head) bool { │ │ 9 + const uri = std.Uri.parseAfterScheme("", head.target) catch return false; │ │10 + const handler, _ = findRoute(uri.path.percent_encoded) orelse return false; │ │11 + return switch (handler) { │ │12 + // a page url can end in "/info/refs" too, but never has a query │ │13 + .get_info_refs => if (uri.query) |q| std.mem.startsWith(u8, q.percent_encoded, "service=") else false, │ │14 + .run_service => head.method == .POST, │ │15 }; │ │25 - │ │26 - const handle = struct { │ │27 - fn h(ctx: Context, stream: std.Io.net.Stream) void { │ │28 - defer stream.close(ctx.io); │ │29 - handleConnection(repo_kind, any_repo_opts, ctx.io, ctx.allocator, ctx.users_dir, ctx.admin_repo_path, stream, ctx.err) catch |request_err| {│ │30 - serve_common.logError(ctx.io, ctx.err, "connection failed: {s}\n", .{@errorName(request_err)}); │ │31 - }; │ │32 - } │ │33 - }.h; │ │34 - │ │35 - serve_common.runListener(io, net_server, tasks, err, "http", Context{ │ │36 - .io = io, │ │37 - .allocator = allocator, │ │38 - .users_dir = users_dir, │ │39 - .admin_repo_path = admin_repo_path, │ │40 - .err = err, │ │41 - }, handle); │ │16 } │ │17 │ │44 -fn handleConnection( │ │45 - comptime repo_kind: rp.RepoKind, │ │46 - comptime any_repo_opts: rp.AnyRepoOpts(repo_kind), │ │18 +pub const GitHandler = *const fn ( │ │19 io: std.Io, │ │20 allocator: std.mem.Allocator, │ │21 users_dir: []const u8, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │22 admin_repo_path: []const u8, │ │51 - stream: std.Io.net.Stream, │ │23 + http_server: *std.http.Server, │ │24 + request: *std.http.Server.Request, │ │25 err: *std.Io.Writer, │ │53 -) !void { │ │54 - var send_buffer: [any_repo_opts.net_buffer_size]u8 = @splat(0); │ │55 - var recv_buffer: [any_repo_opts.net_buffer_size]u8 = @splat(0); │ │56 - var conn_br = stream.reader(io, &recv_buffer); │ │57 - var conn_bw = stream.writer(io, &send_buffer); │ │58 - var http_server = std.http.Server.init(&conn_br.interface, &conn_bw.interface); │ │26 +) anyerror!void; │ │27 │ │60 - if (http_server.reader.state == .ready) { │ │61 - var request = http_server.receiveHead() catch |receive_err| switch (receive_err) { │ │62 - error.HttpConnectionClosing => return, │ │63 - error.ReadFailed => return, │ │64 - else => |e| return e, │ │65 - }; │ │66 - │ │67 - handleGitRequest(repo_kind, any_repo_opts, io, allocator, users_dir, admin_repo_path, &http_server, &request) catch |request_err| { │ │68 - serve_common.logError(io, err, "request failed: {s}\n", .{@errorName(request_err)}); │ │69 - if (http_server.reader.state == .received_head) { │ │70 - http_server.reader.state = .ready; │ │71 - } │ │72 - try writeSimpleResponse(&http_server, 500, "Internal Server Error", "text/plain", @errorName(request_err)); │ │73 - }; │ │74 - try http_server.out.flush(); │ │75 - } │ │28 +// the git request handler for one repo backend, so the web listener needn't be generic │ │29 +pub fn gitHandler(comptime repo_kind: rp.RepoKind, comptime any_repo_opts: rp.AnyRepoOpts(repo_kind)) GitHandler { │ │30 + return struct { │ │31 + fn handle( │ │32 + io: std.Io, │ │33 + allocator: std.mem.Allocator, │ │34 + users_dir: []const u8, │ │35 + admin_repo_path: []const u8, │ │36 + http_server: *std.http.Server, │ │37 + request: *std.http.Server.Request, │ │38 + err: *std.Io.Writer, │ │39 + ) anyerror!void { │ │40 + handleGitRequest(repo_kind, any_repo_opts, io, allocator, users_dir, admin_repo_path, http_server, request) catch |request_err| {│ │41 + serve_common.logError(io, err, "request failed: {s}\n", .{@errorName(request_err)}); │ │42 + if (http_server.reader.state == .received_head) { │ │43 + http_server.reader.state = .ready; │ │44 + } │ │45 + try writeSimpleResponse(http_server, 500, "Internal Server Error", "text/plain", @errorName(request_err)); │ │46 + }; │ │47 + try http_server.out.flush(); │ │48 + } │ │49 + }.handle; │ │50 } │ │51 │ │52 fn handleGitRequest( │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯