diff options
| author | Ian Johnson <ian@ianjohnson.dev> | 2023-11-11 23:15:44 -0500 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2023-11-12 05:11:23 -0500 |
| commit | 7048e93665543005ed3dabd2e7637f01c584006a (patch) | |
| tree | de190dc4347353b472a541e0f9e593251ad2873c /src/Package/Fetch/git.zig | |
| parent | 547481c31c8a538a7badbdce66d81820177ce87f (diff) | |
| download | zig-7048e93665543005ed3dabd2e7637f01c584006a.tar.gz zig-7048e93665543005ed3dabd2e7637f01c584006a.zip | |
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.
Diffstat (limited to 'src/Package/Fetch/git.zig')
| -rw-r--r-- | src/Package/Fetch/git.zig | 54 |
1 files changed, 38 insertions, 16 deletions
diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig index 2a5344eaa4..1fdf5152d6 100644 --- a/src/Package/Fetch/git.zig +++ b/src/Package/Fetch/git.zig @@ -456,6 +456,20 @@ const Packet = union(enum) { }, } } + + /// Returns the normalized form of textual packet data, stripping any + /// trailing '\n'. + /// + /// As documented in + /// [protocol-common](https://git-scm.com/docs/protocol-common#_pkt_line_format), + /// non-binary (textual) pkt-line data should contain a trailing '\n', but + /// is not required to do so (implementations must support both forms). + fn normalizeText(data: []const u8) []const u8 { + return if (mem.endsWith(u8, data, "\n")) + data[0 .. data.len - 1] + else + data; + } }; /// A client session for the Git protocol, currently limited to an HTTP(S) @@ -554,7 +568,7 @@ pub const Session = struct { switch (packet) { .flush => state = .response_start, .data => |data| switch (state) { - .response_start => if (mem.eql(u8, data, "version 2\n")) { + .response_start => if (mem.eql(u8, Packet.normalizeText(data), "version 2")) { return .{ .request = request }; } else { state = .response_content; @@ -573,6 +587,13 @@ pub const Session = struct { const Capability = struct { key: []const u8, value: ?[]const u8 = null, + + fn parse(data: []const u8) Capability { + return if (mem.indexOfScalar(u8, data, '=')) |separator_pos| + .{ .key = data[0..separator_pos], .value = data[separator_pos + 1 ..] } + else + .{ .key = data }; + } }; fn deinit(iterator: *CapabilityIterator) void { @@ -583,13 +604,7 @@ pub const Session = struct { fn next(iterator: *CapabilityIterator) !?Capability { switch (try Packet.read(iterator.request.reader(), &iterator.buf)) { .flush => return null, - .data => |data| if (data.len > 0 and data[data.len - 1] == '\n') { - if (mem.indexOfScalar(u8, data, '=')) |separator_pos| { - return .{ .key = data[0..separator_pos], .value = data[separator_pos + 1 .. data.len - 1] }; - } else { - return .{ .key = data[0 .. data.len - 1] }; - } - } else return error.UnexpectedPacket, + .data => |data| return Capability.parse(Packet.normalizeText(data)), else => return error.UnexpectedPacket, } } @@ -676,18 +691,19 @@ pub const Session = struct { switch (try Packet.read(iterator.request.reader(), &iterator.buf)) { .flush => return null, .data => |data| { - const oid_sep_pos = mem.indexOfScalar(u8, data, ' ') orelse return error.InvalidRefPacket; + const ref_data = Packet.normalizeText(data); + const oid_sep_pos = mem.indexOfScalar(u8, ref_data, ' ') orelse return error.InvalidRefPacket; const oid = parseOid(data[0..oid_sep_pos]) catch return error.InvalidRefPacket; - const name_sep_pos = mem.indexOfAnyPos(u8, data, oid_sep_pos + 1, " \n") orelse return error.InvalidRefPacket; - const name = data[oid_sep_pos + 1 .. name_sep_pos]; + const name_sep_pos = mem.indexOfScalarPos(u8, ref_data, oid_sep_pos + 1, ' ') orelse ref_data.len; + const name = ref_data[oid_sep_pos + 1 .. name_sep_pos]; var symref_target: ?[]const u8 = null; var peeled: ?Oid = null; var last_sep_pos = name_sep_pos; - while (data[last_sep_pos] == ' ') { - const next_sep_pos = mem.indexOfAnyPos(u8, data, last_sep_pos + 1, " \n") orelse return error.InvalidRefPacket; - const attribute = data[last_sep_pos + 1 .. next_sep_pos]; + while (last_sep_pos < ref_data.len) { + const next_sep_pos = mem.indexOfScalarPos(u8, ref_data, last_sep_pos + 1, ' ') orelse ref_data.len; + const attribute = ref_data[last_sep_pos + 1 .. next_sep_pos]; if (mem.startsWith(u8, attribute, "symref-target:")) { symref_target = attribute["symref-target:".len..]; } else if (mem.startsWith(u8, attribute, "peeled:")) { @@ -762,7 +778,7 @@ pub const Session = struct { const packet = try Packet.read(reader, &buf); switch (state) { .section_start => switch (packet) { - .data => |data| if (mem.eql(u8, data, "packfile\n")) { + .data => |data| if (mem.eql(u8, Packet.normalizeText(data), "packfile")) { return .{ .request = request }; } else { state = .section_content; @@ -1462,5 +1478,11 @@ pub fn main() !void { std.debug.print("Starting checkout...\n", .{}); var repository = try Repository.init(allocator, pack_file, index_file); defer repository.deinit(); - try repository.checkout(worktree, commit); + var diagnostics: Diagnostics = .{ .allocator = allocator }; + defer diagnostics.deinit(); + try repository.checkout(worktree, commit, &diagnostics); + + for (diagnostics.errors.items) |err| { + std.debug.print("Diagnostic: {}\n", .{err}); + } } |
