diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-02-23 17:41:38 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-23 17:41:38 -0800 |
| commit | cfce81f7d5f11ab93b2d5fd26df41edf967f333b (patch) | |
| tree | 11e52ad0a44620f4a4519683abd945146c11b312 /src/Package | |
| parent | 7230b68b350b16c637e84f3ff224be24d23214ce (diff) | |
| parent | 653d4158cdcb20be82ff525e122277064e6acb92 (diff) | |
| download | zig-cfce81f7d5f11ab93b2d5fd26df41edf967f333b.tar.gz zig-cfce81f7d5f11ab93b2d5fd26df41edf967f333b.zip | |
Merge pull request #18955 from ziglang/std.http.Server
take std.http in a different direction
Diffstat (limited to 'src/Package')
| -rw-r--r-- | src/Package/Fetch.zig | 85 | ||||
| -rw-r--r-- | src/Package/Fetch/git.zig | 56 |
2 files changed, 71 insertions, 70 deletions
diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index ed3c6b099f..8fbaf79ea5 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -354,7 +354,8 @@ pub fn run(f: *Fetch) RunError!void { .{ path_or_url, @errorName(file_err), @errorName(uri_err) }, )); }; - var resource = try f.initResource(uri); + var server_header_buffer: [header_buffer_size]u8 = undefined; + var resource = try f.initResource(uri, &server_header_buffer); return runResource(f, uri.path, &resource, null); } }, @@ -415,7 +416,8 @@ pub fn run(f: *Fetch) RunError!void { f.location_tok, try eb.printString("invalid URI: {s}", .{@errorName(err)}), ); - var resource = try f.initResource(uri); + var server_header_buffer: [header_buffer_size]u8 = undefined; + var resource = try f.initResource(uri, &server_header_buffer); return runResource(f, uri.path, &resource, remote.hash); } @@ -876,7 +878,9 @@ const FileType = enum { } }; -fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource { +const header_buffer_size = 16 * 1024; + +fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Resource { const gpa = f.arena.child_allocator; const arena = f.arena.allocator(); const eb = &f.error_bundle; @@ -894,10 +898,9 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource { if (ascii.eqlIgnoreCase(uri.scheme, "http") or ascii.eqlIgnoreCase(uri.scheme, "https")) { - var h = std.http.Headers{ .allocator = gpa }; - defer h.deinit(); - - var req = http_client.open(.GET, uri, h, .{}) catch |err| { + var req = http_client.open(.GET, uri, .{ + .server_header_buffer = server_header_buffer, + }) catch |err| { return f.fail(f.location_tok, try eb.printString( "unable to connect to server: {s}", .{@errorName(err)}, @@ -935,7 +938,7 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource { transport_uri.scheme = uri.scheme["git+".len..]; var redirect_uri: []u8 = undefined; var session: git.Session = .{ .transport = http_client, .uri = transport_uri }; - session.discoverCapabilities(gpa, &redirect_uri) catch |err| switch (err) { + session.discoverCapabilities(gpa, &redirect_uri, server_header_buffer) catch |err| switch (err) { error.Redirected => { defer gpa.free(redirect_uri); return f.fail(f.location_tok, try eb.printString( @@ -961,6 +964,7 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource { var ref_iterator = session.listRefs(gpa, .{ .ref_prefixes = &.{ want_ref, want_ref_head, want_ref_tag }, .include_peeled = true, + .server_header_buffer = server_header_buffer, }) catch |err| { return f.fail(f.location_tok, try eb.printString( "unable to list refs: {s}", @@ -1003,7 +1007,7 @@ fn initResource(f: *Fetch, uri: std.Uri) RunError!Resource { _ = std.fmt.bufPrint(&want_oid_buf, "{}", .{ std.fmt.fmtSliceHexLower(&want_oid), }) catch unreachable; - var fetch_stream = session.fetch(gpa, &.{&want_oid_buf}) catch |err| { + var fetch_stream = session.fetch(gpa, &.{&want_oid_buf}, server_header_buffer) catch |err| { return f.fail(f.location_tok, try eb.printString( "unable to create fetch stream: {s}", .{@errorName(err)}, @@ -1036,7 +1040,7 @@ fn unpackResource( .http_request => |req| ft: { // Content-Type takes first precedence. - const content_type = req.response.headers.getFirstValue("Content-Type") orelse + const content_type = req.response.content_type orelse return f.fail(f.location_tok, try eb.addString("missing 'Content-Type' header")); // Extract the MIME type, ignoring charset and boundary directives @@ -1069,7 +1073,7 @@ fn unpackResource( } // Next, the filename from 'content-disposition: attachment' takes precedence. - if (req.response.headers.getFirstValue("Content-Disposition")) |cd_header| { + if (req.response.content_disposition) |cd_header| { break :ft FileType.fromContentDisposition(cd_header) orelse { return f.fail(f.location_tok, try eb.printString( "unsupported Content-Disposition header value: '{s}' for Content-Type=application/octet-stream", @@ -1105,8 +1109,29 @@ fn unpackResource( var dcp = std.compress.gzip.decompressor(br.reader()); try unpackTarball(f, tmp_directory.handle, dcp.reader()); }, - .@"tar.xz" => try unpackTarballCompressed(f, tmp_directory.handle, resource, std.compress.xz), - .@"tar.zst" => try unpackTarballCompressed(f, tmp_directory.handle, resource, ZstdWrapper), + .@"tar.xz" => { + const gpa = f.arena.child_allocator; + const reader = resource.reader(); + var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader); + var dcp = std.compress.xz.decompress(gpa, br.reader()) catch |err| { + return f.fail(f.location_tok, try eb.printString( + "unable to decompress tarball: {s}", + .{@errorName(err)}, + )); + }; + defer dcp.deinit(); + try unpackTarball(f, tmp_directory.handle, dcp.reader()); + }, + .@"tar.zst" => { + const window_size = std.compress.zstd.DecompressorOptions.default_window_buffer_len; + const window_buffer = try f.arena.allocator().create([window_size]u8); + const reader = resource.reader(); + var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader); + var dcp = std.compress.zstd.decompressor(br.reader(), .{ + .window_buffer = window_buffer, + }); + return unpackTarball(f, tmp_directory.handle, dcp.reader()); + }, .git_pack => unpackGitPack(f, tmp_directory.handle, resource) catch |err| switch (err) { error.FetchFailed => return error.FetchFailed, error.OutOfMemory => return error.OutOfMemory, @@ -1118,40 +1143,6 @@ fn unpackResource( } } -// due to slight differences in the API of std.compress.(gzip|xz) and std.compress.zstd, zstd is -// wrapped for generic use in unpackTarballCompressed: see github.com/ziglang/zig/issues/14739 -const ZstdWrapper = struct { - fn DecompressType(comptime T: type) type { - return error{}!std.compress.zstd.DecompressStream(T, .{}); - } - - fn decompress(allocator: Allocator, reader: anytype) DecompressType(@TypeOf(reader)) { - return std.compress.zstd.decompressStream(allocator, reader); - } -}; - -fn unpackTarballCompressed( - f: *Fetch, - out_dir: fs.Dir, - resource: *Resource, - comptime Compression: type, -) RunError!void { - const gpa = f.arena.child_allocator; - const eb = &f.error_bundle; - const reader = resource.reader(); - var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader); - - var decompress = Compression.decompress(gpa, br.reader()) catch |err| { - return f.fail(f.location_tok, try eb.printString( - "unable to decompress tarball: {s}", - .{@errorName(err)}, - )); - }; - defer decompress.deinit(); - - return unpackTarball(f, out_dir, decompress.reader()); -} - fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void { const eb = &f.error_bundle; const gpa = f.arena.child_allocator; diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig index ee8f1ba543..dc0c844d1d 100644 --- a/src/Package/Fetch/git.zig +++ b/src/Package/Fetch/git.zig @@ -494,8 +494,9 @@ pub const Session = struct { session: *Session, allocator: Allocator, redirect_uri: *[]u8, + http_headers_buffer: []u8, ) !void { - var capability_iterator = try session.getCapabilities(allocator, redirect_uri); + var capability_iterator = try session.getCapabilities(allocator, redirect_uri, http_headers_buffer); defer capability_iterator.deinit(); while (try capability_iterator.next()) |capability| { if (mem.eql(u8, capability.key, "agent")) { @@ -521,6 +522,7 @@ pub const Session = struct { session: Session, allocator: Allocator, redirect_uri: *[]u8, + http_headers_buffer: []u8, ) !CapabilityIterator { var info_refs_uri = session.uri; info_refs_uri.path = try std.fs.path.resolvePosix(allocator, &.{ "/", session.uri.path, "info/refs" }); @@ -528,12 +530,13 @@ pub const Session = struct { info_refs_uri.query = "service=git-upload-pack"; info_refs_uri.fragment = null; - var headers = std.http.Headers.init(allocator); - defer headers.deinit(); - try headers.append("Git-Protocol", "version=2"); - - var request = try session.transport.open(.GET, info_refs_uri, headers, .{ - .max_redirects = 3, + const max_redirects = 3; + var request = try session.transport.open(.GET, info_refs_uri, .{ + .redirect_behavior = @enumFromInt(max_redirects), + .server_header_buffer = http_headers_buffer, + .extra_headers = &.{ + .{ .name = "Git-Protocol", .value = "version=2" }, + }, }); errdefer request.deinit(); try request.send(.{}); @@ -541,7 +544,8 @@ pub const Session = struct { try request.wait(); if (request.response.status != .ok) return error.ProtocolError; - if (request.redirects_left < 3) { + const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects; + if (any_redirects_occurred) { if (!mem.endsWith(u8, request.uri.path, "/info/refs")) return error.UnparseableRedirect; var new_uri = request.uri; new_uri.path = new_uri.path[0 .. new_uri.path.len - "/info/refs".len]; @@ -620,6 +624,7 @@ pub const Session = struct { include_symrefs: bool = false, /// Whether to include the peeled object ID for returned tag refs. include_peeled: bool = false, + server_header_buffer: []u8, }; /// Returns an iterator over refs known to the server. @@ -630,11 +635,6 @@ pub const Session = struct { upload_pack_uri.query = null; upload_pack_uri.fragment = null; - var headers = std.http.Headers.init(allocator); - defer headers.deinit(); - try headers.append("Content-Type", "application/x-git-upload-pack-request"); - try headers.append("Git-Protocol", "version=2"); - var body = std.ArrayListUnmanaged(u8){}; defer body.deinit(allocator); const body_writer = body.writer(allocator); @@ -656,8 +656,13 @@ pub const Session = struct { } try Packet.write(.flush, body_writer); - var request = try session.transport.open(.POST, upload_pack_uri, headers, .{ - .handle_redirects = false, + var request = try session.transport.open(.POST, upload_pack_uri, .{ + .redirect_behavior = .unhandled, + .server_header_buffer = options.server_header_buffer, + .extra_headers = &.{ + .{ .name = "Content-Type", .value = "application/x-git-upload-pack-request" }, + .{ .name = "Git-Protocol", .value = "version=2" }, + }, }); errdefer request.deinit(); request.transfer_encoding = .{ .content_length = body.items.len }; @@ -721,18 +726,18 @@ pub const Session = struct { /// Fetches the given refs from the server. A shallow fetch (depth 1) is /// performed if the server supports it. - pub fn fetch(session: Session, allocator: Allocator, wants: []const []const u8) !FetchStream { + pub fn fetch( + session: Session, + allocator: Allocator, + wants: []const []const u8, + http_headers_buffer: []u8, + ) !FetchStream { var upload_pack_uri = session.uri; upload_pack_uri.path = try std.fs.path.resolvePosix(allocator, &.{ "/", session.uri.path, "git-upload-pack" }); defer allocator.free(upload_pack_uri.path); upload_pack_uri.query = null; upload_pack_uri.fragment = null; - var headers = std.http.Headers.init(allocator); - defer headers.deinit(); - try headers.append("Content-Type", "application/x-git-upload-pack-request"); - try headers.append("Git-Protocol", "version=2"); - var body = std.ArrayListUnmanaged(u8){}; defer body.deinit(allocator); const body_writer = body.writer(allocator); @@ -756,8 +761,13 @@ pub const Session = struct { try Packet.write(.{ .data = "done\n" }, body_writer); try Packet.write(.flush, body_writer); - var request = try session.transport.open(.POST, upload_pack_uri, headers, .{ - .handle_redirects = false, + var request = try session.transport.open(.POST, upload_pack_uri, .{ + .redirect_behavior = .not_allowed, + .server_header_buffer = http_headers_buffer, + .extra_headers = &.{ + .{ .name = "Content-Type", .value = "application/x-git-upload-pack-request" }, + .{ .name = "Git-Protocol", .value = "version=2" }, + }, }); errdefer request.deinit(); request.transfer_encoding = .{ .content_length = body.items.len }; |
