aboutsummaryrefslogtreecommitdiff
path: root/src/Package
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-02-23 17:41:38 -0800
committerGitHub <noreply@github.com>2024-02-23 17:41:38 -0800
commitcfce81f7d5f11ab93b2d5fd26df41edf967f333b (patch)
tree11e52ad0a44620f4a4519683abd945146c11b312 /src/Package
parent7230b68b350b16c637e84f3ff224be24d23214ce (diff)
parent653d4158cdcb20be82ff525e122277064e6acb92 (diff)
downloadzig-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.zig85
-rw-r--r--src/Package/Fetch/git.zig56
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 };