authorgravatar for ian@ianjohnson.devIan Johnson <ian@ianjohnson.dev> 2023-11-11 23:15:44-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-12 05:11:23-05:00
log7048e93665543005ed3dabd2e7637f01c584006a
treede190dc4347353b472a541e0f9e593251ad2873c
parent547481c31c8a538a7badbdce66d81820177ce87f

Package.Fetch.git: handle optional pkt-line LF

Addresses a comment in #17779 pointing out the inability to fetch the upstream BoringSSL sources over Git. The reason for this is because the Git server used in this case did not include the optional (but recommended) LF terminator for textual pkt-line data. This commit adjusts handling of textual pkt-line data so that it works both with and without the optional trailing LF.

1 files changed, 38 insertions(+), 16 deletions(-)

src/Package/Fetch/git.zig+38-16
......@@ -456,6 +456,20 @@ const Packet = union(enum) {
456456 },
457457 }
458458 }
459
460 /// Returns the normalized form of textual packet data, stripping any
461 /// trailing '\n'.
462 ///
463 /// As documented in
464 /// [protocol-common](https://git-scm.com/docs/protocol-common#_pkt_line_format),
465 /// non-binary (textual) pkt-line data should contain a trailing '\n', but
466 /// is not required to do so (implementations must support both forms).
467 fn normalizeText(data: []const u8) []const u8 {
468 return if (mem.endsWith(u8, data, "\n"))
469 data[0 .. data.len - 1]
470 else
471 data;
472 }
459473};
460474
461475/// A client session for the Git protocol, currently limited to an HTTP(S)
......@@ -554,7 +568,7 @@ pub const Session = struct {
554568 switch (packet) {
555569 .flush => state = .response_start,
556570 .data => |data| switch (state) {
557 .response_start => if (mem.eql(u8, data, "version 2\n")) {
571 .response_start => if (mem.eql(u8, Packet.normalizeText(data), "version 2")) {
558572 return .{ .request = request };
559573 } else {
560574 state = .response_content;
......@@ -573,6 +587,13 @@ pub const Session = struct {
573587 const Capability = struct {
574588 key: []const u8,
575589 value: ?[]const u8 = null,
590
591 fn parse(data: []const u8) Capability {
592 return if (mem.indexOfScalar(u8, data, '=')) |separator_pos|
593 .{ .key = data[0..separator_pos], .value = data[separator_pos + 1 ..] }
594 else
595 .{ .key = data };
596 }
576597 };
577598
578599 fn deinit(iterator: *CapabilityIterator) void {
......@@ -583,13 +604,7 @@ pub const Session = struct {
583604 fn next(iterator: *CapabilityIterator) !?Capability {
584605 switch (try Packet.read(iterator.request.reader(), &iterator.buf)) {
585606 .flush => return null,
586 .data => |data| if (data.len > 0 and data[data.len - 1] == '\n') {
587 if (mem.indexOfScalar(u8, data, '=')) |separator_pos| {
588 return .{ .key = data[0..separator_pos], .value = data[separator_pos + 1 .. data.len - 1] };
589 } else {
590 return .{ .key = data[0 .. data.len - 1] };
591 }
592 } else return error.UnexpectedPacket,
607 .data => |data| return Capability.parse(Packet.normalizeText(data)),
593608 else => return error.UnexpectedPacket,
594609 }
595610 }
......@@ -676,18 +691,19 @@ pub const Session = struct {
676691 switch (try Packet.read(iterator.request.reader(), &iterator.buf)) {
677692 .flush => return null,
678693 .data => |data| {
679 const oid_sep_pos = mem.indexOfScalar(u8, data, ' ') orelse return error.InvalidRefPacket;
694 const ref_data = Packet.normalizeText(data);
695 const oid_sep_pos = mem.indexOfScalar(u8, ref_data, ' ') orelse return error.InvalidRefPacket;
680696 const oid = parseOid(data[0..oid_sep_pos]) catch return error.InvalidRefPacket;
681697
682 const name_sep_pos = mem.indexOfAnyPos(u8, data, oid_sep_pos + 1, " \n") orelse return error.InvalidRefPacket;
683 const name = data[oid_sep_pos + 1 .. name_sep_pos];
698 const name_sep_pos = mem.indexOfScalarPos(u8, ref_data, oid_sep_pos + 1, ' ') orelse ref_data.len;
699 const name = ref_data[oid_sep_pos + 1 .. name_sep_pos];
684700
685701 var symref_target: ?[]const u8 = null;
686702 var peeled: ?Oid = null;
687703 var last_sep_pos = name_sep_pos;
688 while (data[last_sep_pos] == ' ') {
689 const next_sep_pos = mem.indexOfAnyPos(u8, data, last_sep_pos + 1, " \n") orelse return error.InvalidRefPacket;
690 const attribute = data[last_sep_pos + 1 .. next_sep_pos];
704 while (last_sep_pos < ref_data.len) {
705 const next_sep_pos = mem.indexOfScalarPos(u8, ref_data, last_sep_pos + 1, ' ') orelse ref_data.len;
706 const attribute = ref_data[last_sep_pos + 1 .. next_sep_pos];
691707 if (mem.startsWith(u8, attribute, "symref-target:")) {
692708 symref_target = attribute["symref-target:".len..];
693709 } else if (mem.startsWith(u8, attribute, "peeled:")) {
......@@ -762,7 +778,7 @@ pub const Session = struct {
762778 const packet = try Packet.read(reader, &buf);
763779 switch (state) {
764780 .section_start => switch (packet) {
765 .data => |data| if (mem.eql(u8, data, "packfile\n")) {
781 .data => |data| if (mem.eql(u8, Packet.normalizeText(data), "packfile")) {
766782 return .{ .request = request };
767783 } else {
768784 state = .section_content;
......@@ -1462,5 +1478,11 @@ pub fn main() !void {
14621478 std.debug.print("Starting checkout...\n", .{});
14631479 var repository = try Repository.init(allocator, pack_file, index_file);
14641480 defer repository.deinit();
1465 try repository.checkout(worktree, commit);
1481 var diagnostics: Diagnostics = .{ .allocator = allocator };
1482 defer diagnostics.deinit();
1483 try repository.checkout(worktree, commit, &diagnostics);
1484
1485 for (diagnostics.errors.items) |err| {
1486 std.debug.print("Diagnostic: {}\n", .{err});
1487 }
14661488}