| ... | @@ -644,7 +644,7 @@ pub const Session = struct { | ... | @@ -644,7 +644,7 @@ pub const Session = struct { |
| 644 | supports_agent: bool, | 644 | supports_agent: bool, |
| 645 | supports_shallow: bool, | 645 | supports_shallow: bool, |
| 646 | object_format: Oid.Format, | 646 | object_format: Oid.Format, |
| 647 | allocator: Allocator, | 647 | arena: Allocator, |
| 648 | | 648 | |
| 649 | const agent = "zig/" ++ @import("builtin").zig_version_string; | 649 | const agent = "zig/" ++ @import("builtin").zig_version_string; |
| 650 | const agent_capability = std.fmt.comptimePrint("agent={s}\n", .{agent}); | 650 | const agent_capability = std.fmt.comptimePrint("agent={s}\n", .{agent}); |
| ... | @@ -652,7 +652,7 @@ pub const Session = struct { | ... | @@ -652,7 +652,7 @@ pub const Session = struct { |
| 652 | /// Initializes a client session and discovers the capabilities of the | 652 | /// Initializes a client session and discovers the capabilities of the |
| 653 | /// server for optimal transport. | 653 | /// server for optimal transport. |
| 654 | pub fn init( | 654 | pub fn init( |
| 655 | allocator: Allocator, | 655 | arena: Allocator, |
| 656 | transport: *std.http.Client, | 656 | transport: *std.http.Client, |
| 657 | uri: std.Uri, | 657 | uri: std.Uri, |
| 658 | /// Asserted to be at least `Packet.max_data_length` | 658 | /// Asserted to be at least `Packet.max_data_length` |
| ... | @@ -661,13 +661,12 @@ pub const Session = struct { | ... | @@ -661,13 +661,12 @@ pub const Session = struct { |
| 661 | assert(response_buffer.len >= Packet.max_data_length); | 661 | assert(response_buffer.len >= Packet.max_data_length); |
| 662 | var session: Session = .{ | 662 | var session: Session = .{ |
| 663 | .transport = transport, | 663 | .transport = transport, |
| 664 | .location = try .init(allocator, uri), | 664 | .location = try .init(arena, uri), |
| 665 | .supports_agent = false, | 665 | .supports_agent = false, |
| 666 | .supports_shallow = false, | 666 | .supports_shallow = false, |
| 667 | .object_format = .sha1, | 667 | .object_format = .sha1, |
| 668 | .allocator = allocator, | 668 | .arena = arena, |
| 669 | }; | 669 | }; |
| 670 | errdefer session.deinit(); | | |
| 671 | var capability_iterator: CapabilityIterator = undefined; | 670 | var capability_iterator: CapabilityIterator = undefined; |
| 672 | try session.getCapabilities(&capability_iterator, response_buffer); | 671 | try session.getCapabilities(&capability_iterator, response_buffer); |
| 673 | defer capability_iterator.deinit(); | 672 | defer capability_iterator.deinit(); |
| ... | @@ -690,34 +689,24 @@ pub const Session = struct { | ... | @@ -690,34 +689,24 @@ pub const Session = struct { |
| 690 | return session; | 689 | return session; |
| 691 | } | 690 | } |
| 692 | | 691 | |
| 693 | pub fn deinit(session: *Session) void { | | |
| 694 | session.location.deinit(session.allocator); | | |
| 695 | session.* = undefined; | | |
| 696 | } | | |
| 697 | | | |
| 698 | /// An owned `std.Uri` representing the location of the server (base URI). | 692 | /// An owned `std.Uri` representing the location of the server (base URI). |
| 699 | const Location = struct { | 693 | const Location = struct { |
| 700 | uri: std.Uri, | 694 | uri: std.Uri, |
| 701 | | 695 | |
| 702 | fn init(allocator: Allocator, uri: std.Uri) !Location { | 696 | fn init(arena: Allocator, uri: std.Uri) !Location { |
| 703 | const scheme = try allocator.dupe(u8, uri.scheme); | 697 | const scheme = try arena.dupe(u8, uri.scheme); |
| 704 | errdefer allocator.free(scheme); | 698 | const user = if (uri.user) |user| try std.fmt.allocPrint(arena, "{f}", .{ |
| 705 | const user = if (uri.user) |user| try std.fmt.allocPrint(allocator, "{f}", .{ | | |
| 706 | std.fmt.alt(user, .formatUser), | 699 | std.fmt.alt(user, .formatUser), |
| 707 | }) else null; | 700 | }) else null; |
| 708 | errdefer if (user) |s| allocator.free(s); | 701 | const password = if (uri.password) |password| try std.fmt.allocPrint(arena, "{f}", .{ |
| 709 | const password = if (uri.password) |password| try std.fmt.allocPrint(allocator, "{f}", .{ | | |
| 710 | std.fmt.alt(password, .formatPassword), | 702 | std.fmt.alt(password, .formatPassword), |
| 711 | }) else null; | 703 | }) else null; |
| 712 | errdefer if (password) |s| allocator.free(s); | 704 | const host = if (uri.host) |host| try std.fmt.allocPrint(arena, "{f}", .{ |
| 713 | const host = if (uri.host) |host| try std.fmt.allocPrint(allocator, "{f}", .{ | | |
| 714 | std.fmt.alt(host, .formatHost), | 705 | std.fmt.alt(host, .formatHost), |
| 715 | }) else null; | 706 | }) else null; |
| 716 | errdefer if (host) |s| allocator.free(s); | 707 | const path = try std.fmt.allocPrint(arena, "{f}", .{ |
| 717 | const path = try std.fmt.allocPrint(allocator, "{f}", .{ | | |
| 718 | std.fmt.alt(uri.path, .formatPath), | 708 | std.fmt.alt(uri.path, .formatPath), |
| 719 | }); | 709 | }); |
| 720 | errdefer allocator.free(path); | | |
| 721 | // The query and fragment are not used as part of the base server URI. | 710 | // The query and fragment are not used as part of the base server URI. |
| 722 | return .{ | 711 | return .{ |
| 723 | .uri = .{ | 712 | .uri = .{ |
| ... | @@ -730,14 +719,6 @@ pub const Session = struct { | ... | @@ -730,14 +719,6 @@ pub const Session = struct { |
| 730 | }, | 719 | }, |
| 731 | }; | 720 | }; |
| 732 | } | 721 | } |
| 733 | | | |
| 734 | fn deinit(loc: *Location, allocator: Allocator) void { | | |
| 735 | allocator.free(loc.uri.scheme); | | |
| 736 | if (loc.uri.user) |user| allocator.free(user.percent_encoded); | | |
| 737 | if (loc.uri.password) |password| allocator.free(password.percent_encoded); | | |
| 738 | if (loc.uri.host) |host| allocator.free(host.percent_encoded); | | |
| 739 | allocator.free(loc.uri.path.percent_encoded); | | |
| 740 | } | | |
| 741 | }; | 722 | }; |
| 742 | | 723 | |
| 743 | /// Returns an iterator over capabilities supported by the server. | 724 | /// Returns an iterator over capabilities supported by the server. |
| ... | @@ -745,16 +726,17 @@ pub const Session = struct { | ... | @@ -745,16 +726,17 @@ pub const Session = struct { |
| 745 | /// The `session.location` is updated if the server returns a redirect, so | 726 | /// The `session.location` is updated if the server returns a redirect, so |
| 746 | /// that subsequent session functions do not need to handle redirects. | 727 | /// that subsequent session functions do not need to handle redirects. |
| 747 | fn getCapabilities(session: *Session, it: *CapabilityIterator, response_buffer: []u8) !void { | 728 | fn getCapabilities(session: *Session, it: *CapabilityIterator, response_buffer: []u8) !void { |
| | 729 | const arena = session.arena; |
| 748 | assert(response_buffer.len >= Packet.max_data_length); | 730 | assert(response_buffer.len >= Packet.max_data_length); |
| 749 | var info_refs_uri = session.location.uri; | 731 | var info_refs_uri = session.location.uri; |
| 750 | { | 732 | { |
| 751 | const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ | 733 | const session_uri_path = try std.fmt.allocPrint(arena, "{f}", .{ |
| 752 | std.fmt.alt(session.location.uri.path, .formatPath), | 734 | std.fmt.alt(session.location.uri.path, .formatPath), |
| 753 | }); | 735 | }); |
| 754 | defer session.allocator.free(session_uri_path); | 736 | info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(arena, &.{ |
| 755 | info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "info/refs" }) }; | 737 | "/", session_uri_path, "info/refs", |
| | 738 | }) }; |
| 756 | } | 739 | } |
| 757 | defer session.allocator.free(info_refs_uri.path.percent_encoded); | | |
| 758 | info_refs_uri.query = .{ .percent_encoded = "service=git-upload-pack" }; | 740 | info_refs_uri.query = .{ .percent_encoded = "service=git-upload-pack" }; |
| 759 | info_refs_uri.fragment = null; | 741 | info_refs_uri.fragment = null; |
| 760 | | 742 | |
| ... | @@ -767,6 +749,7 @@ pub const Session = struct { | ... | @@ -767,6 +749,7 @@ pub const Session = struct { |
| 767 | }, | 749 | }, |
| 768 | }), | 750 | }), |
| 769 | .reader = undefined, | 751 | .reader = undefined, |
| | 752 | .decompress = undefined, |
| 770 | }; | 753 | }; |
| 771 | errdefer it.deinit(); | 754 | errdefer it.deinit(); |
| 772 | const request = &it.request; | 755 | const request = &it.request; |
| ... | @@ -777,19 +760,17 @@ pub const Session = struct { | ... | @@ -777,19 +760,17 @@ pub const Session = struct { |
| 777 | if (response.head.status != .ok) return error.ProtocolError; | 760 | if (response.head.status != .ok) return error.ProtocolError; |
| 778 | const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects; | 761 | const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects; |
| 779 | if (any_redirects_occurred) { | 762 | if (any_redirects_occurred) { |
| 780 | const request_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ | 763 | const request_uri_path = try std.fmt.allocPrint(arena, "{f}", .{ |
| 781 | std.fmt.alt(request.uri.path, .formatPath), | 764 | std.fmt.alt(request.uri.path, .formatPath), |
| 782 | }); | 765 | }); |
| 783 | defer session.allocator.free(request_uri_path); | | |
| 784 | if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect; | 766 | if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect; |
| 785 | var new_uri = request.uri; | 767 | var new_uri = request.uri; |
| 786 | new_uri.path = .{ .percent_encoded = request_uri_path[0 .. request_uri_path.len - "/info/refs".len] }; | 768 | new_uri.path = .{ .percent_encoded = request_uri_path[0 .. request_uri_path.len - "/info/refs".len] }; |
| 787 | const new_location: Location = try .init(session.allocator, new_uri); | 769 | session.location = try .init(arena, new_uri); |
| 788 | session.location.deinit(session.allocator); | | |
| 789 | session.location = new_location; | | |
| 790 | } | 770 | } |
| 791 | | 771 | |
| 792 | it.reader = response.reader(response_buffer); | 772 | const decompress_buffer = try arena.alloc(u8, response.head.content_encoding.minBufferCapacity()); |
| | 773 | it.reader = response.readerDecompressing(response_buffer, &it.decompress, decompress_buffer); |
| 793 | var state: enum { response_start, response_content } = .response_start; | 774 | var state: enum { response_start, response_content } = .response_start; |
| 794 | while (true) { | 775 | while (true) { |
| 795 | // Some Git servers (at least GitHub) include an additional | 776 | // Some Git servers (at least GitHub) include an additional |
| ... | @@ -821,6 +802,7 @@ pub const Session = struct { | ... | @@ -821,6 +802,7 @@ pub const Session = struct { |
| 821 | const CapabilityIterator = struct { | 802 | const CapabilityIterator = struct { |
| 822 | request: std.http.Client.Request, | 803 | request: std.http.Client.Request, |
| 823 | reader: *std.Io.Reader, | 804 | reader: *std.Io.Reader, |
| | 805 | decompress: std.http.Decompress, |
| 824 | | 806 | |
| 825 | const Capability = struct { | 807 | const Capability = struct { |
| 826 | key: []const u8, | 808 | key: []const u8, |
| ... | @@ -864,16 +846,15 @@ pub const Session = struct { | ... | @@ -864,16 +846,15 @@ pub const Session = struct { |
| 864 | | 846 | |
| 865 | /// Returns an iterator over refs known to the server. | 847 | /// Returns an iterator over refs known to the server. |
| 866 | pub fn listRefs(session: Session, it: *RefIterator, options: ListRefsOptions) !void { | 848 | pub fn listRefs(session: Session, it: *RefIterator, options: ListRefsOptions) !void { |
| | 849 | const arena = session.arena; |
| 867 | assert(options.buffer.len >= Packet.max_data_length); | 850 | assert(options.buffer.len >= Packet.max_data_length); |
| 868 | var upload_pack_uri = session.location.uri; | 851 | var upload_pack_uri = session.location.uri; |
| 869 | { | 852 | { |
| 870 | const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ | 853 | const session_uri_path = try std.fmt.allocPrint(arena, "{f}", .{ |
| 871 | std.fmt.alt(session.location.uri.path, .formatPath), | 854 | std.fmt.alt(session.location.uri.path, .formatPath), |
| 872 | }); | 855 | }); |
| 873 | defer session.allocator.free(session_uri_path); | 856 | upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(arena, &.{ "/", session_uri_path, "git-upload-pack" }) }; |
| 874 | upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; | | |
| 875 | } | 857 | } |
| 876 | defer session.allocator.free(upload_pack_uri.path.percent_encoded); | | |
| 877 | upload_pack_uri.query = null; | 858 | upload_pack_uri.query = null; |
| 878 | upload_pack_uri.fragment = null; | 859 | upload_pack_uri.fragment = null; |
| 879 | | 860 | |
| ... | @@ -883,16 +864,14 @@ pub const Session = struct { | ... | @@ -883,16 +864,14 @@ pub const Session = struct { |
| 883 | try Packet.write(.{ .data = agent_capability }, &body); | 864 | try Packet.write(.{ .data = agent_capability }, &body); |
| 884 | } | 865 | } |
| 885 | { | 866 | { |
| 886 | const object_format_packet = try std.fmt.allocPrint(session.allocator, "object-format={t}\n", .{ | 867 | const object_format_packet = try std.fmt.allocPrint(arena, "object-format={t}\n", .{ |
| 887 | session.object_format, | 868 | session.object_format, |
| 888 | }); | 869 | }); |
| 889 | defer session.allocator.free(object_format_packet); | | |
| 890 | try Packet.write(.{ .data = object_format_packet }, &body); | 870 | try Packet.write(.{ .data = object_format_packet }, &body); |
| 891 | } | 871 | } |
| 892 | try Packet.write(.delimiter, &body); | 872 | try Packet.write(.delimiter, &body); |
| 893 | for (options.ref_prefixes) |ref_prefix| { | 873 | for (options.ref_prefixes) |ref_prefix| { |
| 894 | const ref_prefix_packet = try std.fmt.allocPrint(session.allocator, "ref-prefix {s}\n", .{ref_prefix}); | 874 | const ref_prefix_packet = try std.fmt.allocPrint(arena, "ref-prefix {s}\n", .{ref_prefix}); |
| 895 | defer session.allocator.free(ref_prefix_packet); | | |
| 896 | try Packet.write(.{ .data = ref_prefix_packet }, &body); | 875 | try Packet.write(.{ .data = ref_prefix_packet }, &body); |
| 897 | } | 876 | } |
| 898 | if (options.include_symrefs) { | 877 | if (options.include_symrefs) { |
| ... | @@ -913,6 +892,7 @@ pub const Session = struct { | ... | @@ -913,6 +892,7 @@ pub const Session = struct { |
| 913 | }), | 892 | }), |
| 914 | .reader = undefined, | 893 | .reader = undefined, |
| 915 | .format = session.object_format, | 894 | .format = session.object_format, |
| | 895 | .decompress = undefined, |
| 916 | }; | 896 | }; |
| 917 | const request = &it.request; | 897 | const request = &it.request; |
| 918 | errdefer request.deinit(); | 898 | errdefer request.deinit(); |
| ... | @@ -920,13 +900,15 @@ pub const Session = struct { | ... | @@ -920,13 +900,15 @@ pub const Session = struct { |
| 920 | | 900 | |
| 921 | var response = try request.receiveHead(options.buffer); | 901 | var response = try request.receiveHead(options.buffer); |
| 922 | if (response.head.status != .ok) return error.ProtocolError; | 902 | if (response.head.status != .ok) return error.ProtocolError; |
| 923 | it.reader = response.reader(options.buffer); | 903 | const decompress_buffer = try arena.alloc(u8, response.head.content_encoding.minBufferCapacity()); |
| | 904 | it.reader = response.readerDecompressing(options.buffer, &it.decompress, decompress_buffer); |
| 924 | } | 905 | } |
| 925 | | 906 | |
| 926 | pub const RefIterator = struct { | 907 | pub const RefIterator = struct { |
| 927 | format: Oid.Format, | 908 | format: Oid.Format, |
| 928 | request: std.http.Client.Request, | 909 | request: std.http.Client.Request, |
| 929 | reader: *std.Io.Reader, | 910 | reader: *std.Io.Reader, |
| | 911 | decompress: std.http.Decompress, |
| 930 | | 912 | |
| 931 | pub const Ref = struct { | 913 | pub const Ref = struct { |
| 932 | oid: Oid, | 914 | oid: Oid, |
| ... | @@ -981,16 +963,15 @@ pub const Session = struct { | ... | @@ -981,16 +963,15 @@ pub const Session = struct { |
| 981 | /// Asserted to be at least `Packet.max_data_length`. | 963 | /// Asserted to be at least `Packet.max_data_length`. |
| 982 | response_buffer: []u8, | 964 | response_buffer: []u8, |
| 983 | ) !void { | 965 | ) !void { |
| | 966 | const arena = session.arena; |
| 984 | assert(response_buffer.len >= Packet.max_data_length); | 967 | assert(response_buffer.len >= Packet.max_data_length); |
| 985 | var upload_pack_uri = session.location.uri; | 968 | var upload_pack_uri = session.location.uri; |
| 986 | { | 969 | { |
| 987 | const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ | 970 | const session_uri_path = try std.fmt.allocPrint(arena, "{f}", .{ |
| 988 | std.fmt.alt(session.location.uri.path, .formatPath), | 971 | std.fmt.alt(session.location.uri.path, .formatPath), |
| 989 | }); | 972 | }); |
| 990 | defer session.allocator.free(session_uri_path); | 973 | upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(arena, &.{ "/", session_uri_path, "git-upload-pack" }) }; |
| 991 | upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; | | |
| 992 | } | 974 | } |
| 993 | defer session.allocator.free(upload_pack_uri.path.percent_encoded); | | |
| 994 | upload_pack_uri.query = null; | 975 | upload_pack_uri.query = null; |
| 995 | upload_pack_uri.fragment = null; | 976 | upload_pack_uri.fragment = null; |
| 996 | | 977 | |
| ... | @@ -1000,8 +981,7 @@ pub const Session = struct { | ... | @@ -1000,8 +981,7 @@ pub const Session = struct { |
| 1000 | try Packet.write(.{ .data = agent_capability }, &body); | 981 | try Packet.write(.{ .data = agent_capability }, &body); |
| 1001 | } | 982 | } |
| 1002 | { | 983 | { |
| 1003 | const object_format_packet = try std.fmt.allocPrint(session.allocator, "object-format={s}\n", .{@tagName(session.object_format)}); | 984 | const object_format_packet = try std.fmt.allocPrint(arena, "object-format={s}\n", .{@tagName(session.object_format)}); |
| 1004 | defer session.allocator.free(object_format_packet); | | |
| 1005 | try Packet.write(.{ .data = object_format_packet }, &body); | 985 | try Packet.write(.{ .data = object_format_packet }, &body); |
| 1006 | } | 986 | } |
| 1007 | try Packet.write(.delimiter, &body); | 987 | try Packet.write(.delimiter, &body); |
| ... | @@ -1031,6 +1011,7 @@ pub const Session = struct { | ... | @@ -1031,6 +1011,7 @@ pub const Session = struct { |
| 1031 | .input = undefined, | 1011 | .input = undefined, |
| 1032 | .reader = undefined, | 1012 | .reader = undefined, |
| 1033 | .remaining_len = undefined, | 1013 | .remaining_len = undefined, |
| | 1014 | .decompress = undefined, |
| 1034 | }; | 1015 | }; |
| 1035 | const request = &fs.request; | 1016 | const request = &fs.request; |
| 1036 | errdefer request.deinit(); | 1017 | errdefer request.deinit(); |
| ... | @@ -1040,7 +1021,8 @@ pub const Session = struct { | ... | @@ -1040,7 +1021,8 @@ pub const Session = struct { |
| 1040 | var response = try request.receiveHead(&.{}); | 1021 | var response = try request.receiveHead(&.{}); |
| 1041 | if (response.head.status != .ok) return error.ProtocolError; | 1022 | if (response.head.status != .ok) return error.ProtocolError; |
| 1042 | | 1023 | |
| 1043 | const reader = response.reader(response_buffer); | 1024 | const decompress_buffer = try arena.alloc(u8, response.head.content_encoding.minBufferCapacity()); |
| | 1025 | const reader = response.readerDecompressing(response_buffer, &fs.decompress, decompress_buffer); |
| 1044 | // We are not interested in any of the sections of the returned fetch | 1026 | // We are not interested in any of the sections of the returned fetch |
| 1045 | // data other than the packfile section, since we aren't doing anything | 1027 | // data other than the packfile section, since we aren't doing anything |
| 1046 | // complex like ref negotiation (this is a fresh clone). | 1028 | // complex like ref negotiation (this is a fresh clone). |
| ... | @@ -1079,6 +1061,7 @@ pub const Session = struct { | ... | @@ -1079,6 +1061,7 @@ pub const Session = struct { |
| 1079 | reader: std.Io.Reader, | 1061 | reader: std.Io.Reader, |
| 1080 | err: ?Error = null, | 1062 | err: ?Error = null, |
| 1081 | remaining_len: usize, | 1063 | remaining_len: usize, |
| | 1064 | decompress: std.http.Decompress, |
| 1082 | | 1065 | |
| 1083 | pub fn deinit(fs: *FetchStream) void { | 1066 | pub fn deinit(fs: *FetchStream) void { |
| 1084 | fs.request.deinit(); | 1067 | fs.request.deinit(); |