╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───╮ │src/test.zig│ ╰────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │217 try sshd_file.setPermissions(io, .executable_file);│ │218 } │ │219 } │ │285 - │ │286 - if (use_serve_command) { │ │287 - comptime { │ │288 - std.debug.assert(server_repo_kind == .xit); │ │289 - } │ │290 - return .{ │ │291 - .core = .{ │ │292 - .io = io, │ │293 - .allocator = allocator, │ │294 - .sshd_process = null, │ │295 - .serve_process = null, │ │296 - }, │ │297 - }; │ │298 - } else { │ │299 - return .{ │ │300 - .core = .{ .io = io, .process = null }, │ │301 - }; │ │302 - } │ │220 + return .{ │ │221 + .core = .{ │ │222 + .io = io, │ │223 + .allocator = allocator, │ │224 + .sshd_process = null, │ │225 + .serve_process = null, │ │226 + }, │ │227 + }; │ │228 }, │ │229 }, │ │230 } │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │231 } │ │232 │ │308 - fn start(self: *Server(server_repo_kind, transport_def, temp_dir_name, port, use_serve_command)) !void {│ │233 + fn start(self: *Server(transport_def, temp_dir_name, port)) !void { │ │234 switch (transport_def) { │ │235 .file => {}, │ │236 .wire => |wire_kind| switch (wire_kind) { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │237 .http => { │ │313 - if (use_serve_command) { │ │314 - std.debug.assert(self.core.process == null); │ │238 + std.debug.assert(self.core.process == null); │ │239 │ │316 - const cwd_path = try std.process.currentPathAlloc(self.core.io, self.core.allocator); │ │317 - defer self.core.allocator.free(cwd_path); │ │318 - const haxy_path = try std.fs.path.join(self.core.allocator, &.{ cwd_path, "zig-out/bin/haxy" }); │ │319 - defer self.core.allocator.free(haxy_path); │ │320 - const listen_arg = std.fmt.comptimePrint("127.0.0.1:{}", .{port}); │ │240 + const cwd_path = try std.process.currentPathAlloc(self.core.io, self.core.allocator); │ │241 + defer self.core.allocator.free(cwd_path); │ │242 + const haxy_path = try std.fs.path.join(self.core.allocator, &.{ cwd_path, "zig-out/bin/haxy" }); │ │243 + defer self.core.allocator.free(haxy_path); │ │244 + const listen_arg = std.fmt.comptimePrint("127.0.0.1:{}", .{port}); │ │245 │ │322 - self.core.process = try std.process.spawn(self.core.io, .{ │ │323 - .argv = &.{ haxy_path, "serve", "--http-listen", listen_arg, "--project-root", temp_dir_name }, │ │324 - .stdin = .ignore, │ │325 - .stdout = .ignore, │ │326 - .stderr = .ignore, │ │327 - }); │ │328 - } else { │ │329 - const ServerHandler = struct { │ │330 - fn run(core: *Core) !void { │ │331 - var send_buffer = [_]u8{0} ** 1024; │ │332 - var recv_buffer = [_]u8{0} ** 1024; │ │333 - │ │334 - accept: while (true) { │ │335 - const stream = try core.net_server.accept(core.io); │ │336 - defer stream.close(core.io); │ │337 - │ │338 - var conn_br = stream.reader(core.io, &recv_buffer); │ │339 - var conn_bw = stream.writer(core.io, &send_buffer); │ │340 - var http_server = std.http.Server.init(&conn_br.interface, &conn_bw.interface); │ │341 - │ │342 - while (http_server.reader.state == .ready) { │ │343 - var request = http_server.receiveHead() catch |err| switch (err) { │ │344 - error.HttpConnectionClosing => continue :accept, │ │345 - else => |e| return e, │ │346 - }; │ │347 - if (std.mem.eql(u8, request.head.target, "/stop-server")) { │ │348 - break :accept; │ │349 - } │ │350 - │ │351 - const uri = try std.Uri.parseAfterScheme("", request.head.target); │ │352 - if (uri.path.percent_encoded[0] != '/') return error.PathMustStartWithSlash; │ │353 - const path = if (std.mem.indexOfScalar(u8, uri.path.percent_encoded[1..], '/')) |idx| │ │354 - uri.path.percent_encoded[idx + 1 ..] │ │355 - else │ │356 - return error.SlashNotFound; │ │357 - │ │358 - const cwd_path = try std.process.currentPathAlloc(core.io, core.allocator); │ │359 - defer core.allocator.free(cwd_path); │ │360 - const temp_dir_path = try std.fs.path.join(core.allocator, &.{ cwd_path, core.temp_dir_name }); │ │361 - defer core.allocator.free(temp_dir_path); │ │362 - const path_translated = try std.fmt.allocPrint(core.allocator, "{s}{s}", .{ │ │363 - temp_dir_path, │ │364 - uri.path.percent_encoded, │ │365 - }); │ │366 - defer core.allocator.free(path_translated); │ │367 - │ │368 - // init env map │ │369 - var env_map = std.process.Environ.Map.init(core.allocator); │ │370 - defer env_map.deinit(); │ │371 - try env_map.put("GATEWAY_INTERFACE", "CGI/1.1"); │ │372 - try env_map.put("REQUEST_METHOD", @tagName(request.head.method)); │ │373 - try env_map.put("PATH_INFO", path); │ │374 - try env_map.put("PATH_TRANSLATED", path_translated); │ │375 - if (uri.query) |query| { │ │376 - try env_map.put("QUERY_STRING", query.percent_encoded); │ │377 - } │ │378 - │ │379 - var accept: std.ArrayList([]const u8) = .empty; │ │380 - defer accept.deinit(core.allocator); │ │381 - │ │382 - var keep_alive = true; // HTTP 1.1 defaults to keep-alive │ │383 - │ │384 - // iterate over headers to fill env map │ │385 - var req_header_it = request.iterateHeaders(); │ │386 - while (req_header_it.next()) |header| { │ │387 - const header_name = header.name; │ │388 - const header_value = header.value; │ │389 - │ │390 - if (std.ascii.eqlIgnoreCase(header_name, "content-type")) { │ │391 - try env_map.put("CONTENT_TYPE", header_value); │ │392 - } else if (std.ascii.eqlIgnoreCase(header_name, "content-length")) { │ │393 - try env_map.put("CONTENT_LENGTH", header_value); │ │394 - } else if (std.ascii.eqlIgnoreCase(header_name, "referer")) { │ │395 - try env_map.put("HTTP_REFERER", header_value); │ │396 - } else if (std.ascii.eqlIgnoreCase(header_name, "accept")) { │ │397 - try accept.append(core.allocator, header_value); │ │398 - } else if (std.ascii.eqlIgnoreCase(header_name, "user-agent")) { │ │399 - try env_map.put("HTTP_USER_AGENT", header_value); │ │400 - } else if (std.ascii.eqlIgnoreCase(header_name, "connection")) { │ │401 - if (std.ascii.eqlIgnoreCase(header_value, "close")) { │ │402 - keep_alive = false; │ │403 - } │ │404 - } else if (std.ascii.eqlIgnoreCase(header_name, "git-protocol")) { │ │405 - try env_map.put("GIT_PROTOCOL", header_value); │ │406 - } │ │407 - } │ │408 - │ │409 - const accept_str = try std.mem.join(core.allocator, ",", accept.items); │ │410 - defer core.allocator.free(accept_str); │ │411 - if (accept_str.len > 0) { │ │412 - try env_map.put("HTTP_ACCEPT", accept_str); │ │413 - } │ │414 - │ │415 - var process = try std.process.spawn(core.io, .{ │ │416 - .argv = switch (server_repo_kind) { │ │417 - .git => &.{ "git", "http-backend" }, │ │418 - .xit => &.{ "../zig-out/bin/haxy", "http-backend" }, │ │419 - }, │ │420 - .cwd = switch (server_repo_kind) { │ │421 - .git => .inherit, │ │422 - .xit => .{ .path = temp_dir_name }, │ │423 - }, │ │424 - .environ_map = &env_map, │ │425 - .stdin = .pipe, │ │426 - .stdout = .pipe, │ │427 - .stderr = .pipe, │ │428 - }); │ │429 - defer process.kill(core.io); │ │430 - │ │431 - if (request.head.method == .POST) { │ │432 - const reader = try request.readerExpectContinue(&.{}); │ │433 - const request_body = try reader.allocRemaining(core.allocator, .unlimited); │ │434 - defer core.allocator.free(request_body); │ │435 - try process.stdin.?.writeStreamingAll(core.io, request_body); │ │436 - } │ │437 - process.stdin.?.close(core.io); │ │438 - process.stdin = null; │ │439 - │ │440 - var multi_reader_buffer: std.Io.File.MultiReader.Buffer(2) = undefined; │ │441 - var multi_reader: std.Io.File.MultiReader = undefined; │ │442 - multi_reader.init(core.allocator, core.io, multi_reader_buffer.toStreams(), &.{ process.stdout.?, process.stderr.? }); │ │443 - defer multi_reader.deinit(); │ │444 - │ │445 - while (multi_reader.fill(64, .none)) |_| {} else |err| switch (err) { │ │446 - error.EndOfStream => {}, │ │447 - else => |e| return e, │ │448 - } │ │449 - │ │450 - try multi_reader.checkAnyError(); │ │451 - │ │452 - _ = try process.wait(core.io); │ │453 - │ │454 - const stdout_slice = try multi_reader.toOwnedSlice(0); │ │455 - defer core.allocator.free(stdout_slice); │ │456 - const stderr_slice = try multi_reader.toOwnedSlice(1); │ │457 - defer core.allocator.free(stderr_slice); │ │458 - │ │459 - // transition the http state machine so it can │ │460 - // read the next request on this connection │ │461 - if (http_server.reader.state == .received_head) { │ │462 - http_server.reader.state = .ready; │ │463 - } │ │464 - │ │465 - if (stderr_slice.len > 0) { │ │466 - std.debug.print("Error from git-http-backend:\n{s}\n", .{stderr_slice}); │ │467 - try http_server.out.writeAll("HTTP/1.1 500 Internal Server Error\r\n\r\n"); │ │468 - } else { │ │469 - try http_server.out.writeAll("HTTP/1.1 200 OK\r\n"); │ │470 - const double_newline = "\r\n\r\n"; │ │471 - const double_newline_idx = std.mem.indexOf(u8, stdout_slice, double_newline) orelse unreachable; │ │472 - try http_server.out.writeAll(stdout_slice[0..double_newline_idx]); │ │473 - try http_server.out.print("\r\nContent-Length: {}", .{stdout_slice.len - (double_newline_idx + double_newline.len)});│ │474 - try http_server.out.writeAll(stdout_slice[double_newline_idx..]); │ │475 - } │ │476 - try http_server.out.flush(); │ │477 - │ │478 - if (!keep_alive) { │ │479 - continue :accept; │ │480 - } │ │481 - } │ │482 - } │ │483 - } │ │484 - }; │ │485 - self.core.server_thread = try std.Thread.spawn(.{}, ServerHandler.run, .{&self.core}); │ │486 - } │ │246 + self.core.process = try std.process.spawn(self.core.io, .{ │ │247 + .argv = &.{ haxy_path, "serve", "--http-listen", listen_arg, "--project-root", temp_dir_name }, │ │248 + .stdin = .ignore, │ │249 + .stdout = .ignore, │ │250 + .stderr = .ignore, │ │251 + }); │ │252 }, │ │253 .raw => unreachable, │ │254 .ssh => { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │490 - if (use_serve_command) { │ │491 - std.debug.assert(self.core.sshd_process == null); │ │492 - std.debug.assert(self.core.serve_process == null); │ │493 - │ │494 - const cwd_path = try std.process.currentPathAlloc(self.core.io, self.core.allocator); │ │495 - defer self.core.allocator.free(cwd_path); │ │496 - const haxy_path = try std.fs.path.join(self.core.allocator, &.{ cwd_path, "zig-out/bin/haxy" }); │ │497 - defer self.core.allocator.free(haxy_path); │ │498 - const ssh_listen_arg = std.fmt.comptimePrint("127.0.0.1:{}", .{port + 1000}); │ │499 - const http_listen_arg = std.fmt.comptimePrint("127.0.0.1:{}", .{port + 2000}); │ │255 + std.debug.assert(self.core.sshd_process == null); │ │256 + std.debug.assert(self.core.serve_process == null); │ │257 │ │501 - self.core.serve_process = try std.process.spawn(self.core.io, .{ │ │502 - .argv = &.{ haxy_path, "serve", "--http-listen", http_listen_arg, "--ssh-listen", ssh_listen_arg, "--project-root", temp_dir_name },│ │503 - .stdin = .ignore, │ │504 - .stdout = .ignore, │ │505 - .stderr = .ignore, │ │506 - }); │ │258 + const cwd_path = try std.process.currentPathAlloc(self.core.io, self.core.allocator); │ │259 + defer self.core.allocator.free(cwd_path); │ │260 + const haxy_path = try std.fs.path.join(self.core.allocator, &.{ cwd_path, "zig-out/bin/haxy" }); │ │261 + defer self.core.allocator.free(haxy_path); │ │262 + const ssh_listen_arg = std.fmt.comptimePrint("127.0.0.1:{}", .{port + 1000}); │ │263 + const http_listen_arg = std.fmt.comptimePrint("127.0.0.1:{}", .{port + 2000}); │ │264 │ │508 - const serve_address = try std.Io.net.IpAddress.parseIp4("127.0.0.1", port + 1000); │ │509 - for (0..50) |_| { │ │510 - const stream = serve_address.connect(self.core.io, .{ .mode = .stream }) catch { │ │511 - try std.Io.sleep(self.core.io, .fromMilliseconds(100), .real); │ │512 - continue; │ │513 - }; │ │514 - var probe_buf = [_]u8{0} ** 16; │ │515 - var probe_writer = stream.writer(self.core.io, &probe_buf); │ │516 - try probe_writer.interface.writeAll("probe\n"); │ │517 - try probe_writer.interface.flush(); │ │518 - stream.close(self.core.io); │ │519 - break; │ │520 - } │ │265 + self.core.serve_process = try std.process.spawn(self.core.io, .{ │ │266 + .argv = &.{ haxy_path, "serve", "--http-listen", http_listen_arg, "--ssh-listen", ssh_listen_arg, "--project-root", temp_dir_name }, │ │267 + .stdin = .ignore, │ │268 + .stdout = .ignore, │ │269 + .stderr = .ignore, │ │270 + }); │ │271 │ │522 - self.core.sshd_process = try std.process.spawn(self.core.io, .{ │ │523 - .argv = &.{"./sshd.sh"}, │ │524 - .cwd = .{ .path = temp_dir_name }, │ │525 - .stdin = .pipe, │ │526 - .stdout = .ignore, │ │527 - .stderr = .ignore, │ │528 - }); │ │529 - } else { │ │530 - std.debug.assert(self.core.process == null); │ │531 - self.core.process = try std.process.spawn(self.core.io, .{ │ │532 - .argv = &.{"./sshd.sh"}, │ │533 - .cwd = .{ .path = temp_dir_name }, │ │534 - .stdin = .pipe, │ │535 - .stdout = .ignore, │ │536 - .stderr = .ignore, │ │537 - }); │ │272 + const serve_address = try std.Io.net.IpAddress.parseIp4("127.0.0.1", port + 1000); │ │273 + for (0..50) |_| { │ │274 + const stream = serve_address.connect(self.core.io, .{ .mode = .stream }) catch { │ │275 + try std.Io.sleep(self.core.io, .fromMilliseconds(100), .real); │ │276 + continue; │ │277 + }; │ │278 + var probe_buf = [_]u8{0} ** 16; │ │279 + var probe_writer = stream.writer(self.core.io, &probe_buf); │ │280 + try probe_writer.interface.writeAll("probe\n"); │ │281 + try probe_writer.interface.flush(); │ │282 + stream.close(self.core.io); │ │283 + break; │ │284 } │ │285 + │ │286 + self.core.sshd_process = try std.process.spawn(self.core.io, .{ │ │287 + .argv = &.{"./sshd.sh"}, │ │288 + .cwd = .{ .path = temp_dir_name }, │ │289 + .stdin = .pipe, │ │290 + .stdout = .ignore, │ │291 + .stderr = .ignore, │ │292 + }); │ │293 }, │ │294 }, │ │295 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │296 │ │543 - // wait for server to be ready by polling the port │ │297 if (transport_def == .wire) { │ │298 const address = try std.Io.net.IpAddress.parseIp4("127.0.0.1", port);│ │299 for (0..50) |_| { │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │307 } │ │308 } │ │309 │ │557 - fn stop(self: *Server(server_repo_kind, transport_def, temp_dir_name, port, use_serve_command)) void {│ │310 + fn stop(self: *Server(transport_def, temp_dir_name, port)) void { │ │311 switch (transport_def) { │ │312 .file => {}, │ │313 .wire => |wire_kind| switch (wire_kind) { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │561 - .http => { │ │562 - if (use_serve_command) { │ │563 - if (self.core.process) |*process| { │ │564 - _ = process.kill(self.core.io); │ │565 - self.core.process = null; │ │566 - } │ │567 - return; │ │568 - } │ │569 - │ │570 - var client = std.http.Client{ .io = self.core.io, .allocator = self.core.allocator }; │ │571 - defer client.deinit(); │ │572 - _ = client.fetch(.{ .location = .{ .url = self.core.stop_server_endpoint } }) catch return;│ │573 - self.core.server_thread.join(); │ │314 + .http => if (self.core.process) |*process| { │ │315 + _ = process.kill(self.core.io); │ │316 + self.core.process = null; │ │317 }, │ │318 .raw => unreachable, │ │319 .ssh => { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │577 - if (use_serve_command) { │ │578 - if (self.core.sshd_process) |*process| { │ │579 - _ = process.kill(self.core.io); │ │580 - self.core.sshd_process = null; │ │581 - } │ │582 - if (self.core.serve_process) |*process| { │ │583 - _ = process.kill(self.core.io); │ │584 - self.core.serve_process = null; │ │585 - } │ │586 - } else { │ │587 - _ = self.core.process.?.kill(self.core.io);│ │588 - self.core.process = null; │ │320 + if (self.core.sshd_process) |*process| { │ │321 + _ = process.kill(self.core.io); │ │322 + self.core.sshd_process = null; │ │323 + } │ │324 + if (self.core.serve_process) |*process| { │ │325 + _ = process.kill(self.core.io); │ │326 + self.core.serve_process = null; │ │327 } │ │328 }, │ │329 }, │ ╰────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────╮ │333 } │ │334 │ │335 fn uploadPackCommand( │ │598 - comptime server_repo_kind: rp.RepoKind,│ │336 comptime is_ssh: bool, │ │600 - comptime use_serve_command: bool, │ │337 allocator: std.mem.Allocator, │ │338 cwd_path: []const u8, │ │339 comptime port: u16, │ ╰────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │340 ) ![]const u8 { │ │605 - return switch (server_repo_kind) { │ │606 - .xit => if (is_ssh and use_serve_command) │ │607 - std.fmt.allocPrint(allocator, "{s}/zig-out/bin/haxy ssh-helper --ssh-connect 127.0.0.1:{} --service upload-pack", .{ cwd_path, port + 1000 })│ │608 - else │ │609 - std.fmt.allocPrint(allocator, "{s}/zig-out/bin/haxy upload-pack", .{cwd_path}), │ │610 - .git => allocator.dupe(u8, "git-upload-pack"), │ │611 - }; │ │341 + return if (is_ssh) │ │342 + std.fmt.allocPrint(allocator, "{s}/zig-out/bin/haxy ssh-helper --ssh-connect 127.0.0.1:{} --service upload-pack", .{ cwd_path, port + 1000 }) │ │343 + else │ │344 + allocator.dupe(u8, "git-upload-pack"); │ │345 } │ │346 │ │347 fn receivePackCommand( │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯