| author | |
| committer | |
| log | 1ba6838bc3919f2e1306b01a11fd3d5dd01fefe1 |
| tree | 15eb64ba704c52c178f49db30d3fe50a0a27a8b5 |
| parent | d2f77920398976670aa25cb5fd278435d08eba25 |
| parent | 8da645c883c3477ef21c72603434f130c1c43e65 |
| signature |
fetch, tls, and http fixes7 files changed, 172 insertions(+), 155 deletions(-)
lib/std/Io/Reader.zig+1-3| ... | @@ -25,9 +25,7 @@ pub const VTable = struct { | ... | @@ -25,9 +25,7 @@ pub const VTable = struct { |
| 25 | /// | 25 | /// |
| 26 | /// Returns the number of bytes written, which will be at minimum `0` and | 26 | /// Returns the number of bytes written, which will be at minimum `0` and |
| 27 | /// at most `limit`. The number returned, including zero, does not indicate | 27 | /// at most `limit`. The number returned, including zero, does not indicate |
| 28 | /// end of stream. `limit` is guaranteed to be at least as large as the | 28 | /// end of stream. |
| 29 | /// buffer capacity of `w`, a value whose minimum size is determined by the | ||
| 30 | /// stream implementation. | ||
| 31 | /// | 29 | /// |
| 32 | /// The reader's internal logical seek position moves forward in accordance | 30 | /// The reader's internal logical seek position moves forward in accordance |
| 33 | /// with the number of bytes returned from this function. | 31 | /// with the number of bytes returned from this function. |
lib/std/crypto/tls/Client.zig+45-23| ... | @@ -61,9 +61,6 @@ pub const ReadError = error{ | ... | @@ -61,9 +61,6 @@ pub const ReadError = error{ |
| 61 | TlsUnexpectedMessage, | 61 | TlsUnexpectedMessage, |
| 62 | TlsIllegalParameter, | 62 | TlsIllegalParameter, |
| 63 | TlsSequenceOverflow, | 63 | TlsSequenceOverflow, |
| 64 | /// The buffer provided to the read function was not at least | ||
| 65 | /// `min_buffer_len`. | ||
| 66 | OutputBufferUndersize, | ||
| 67 | }; | 64 | }; |
| 68 | 65 | ||
| 69 | pub const SslKeyLog = struct { | 66 | pub const SslKeyLog = struct { |
| ... | @@ -372,7 +369,8 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client | ... | @@ -372,7 +369,8 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 372 | }; | 369 | }; |
| 373 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, pv.server_handshake_key) catch | 370 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, pv.server_handshake_key) catch |
| 374 | return error.TlsBadRecordMac; | 371 | return error.TlsBadRecordMac; |
| 375 | cleartext_fragment_end += std.mem.trimEnd(u8, cleartext, "\x00").len; | 372 | // TODO use scalar, non-slice version |
| 373 | cleartext_fragment_end += mem.trimEnd(u8, cleartext, "\x00").len; | ||
| 376 | }, | 374 | }, |
| 377 | } | 375 | } |
| 378 | read_seq += 1; | 376 | read_seq += 1; |
| ... | @@ -395,9 +393,9 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client | ... | @@ -395,9 +393,9 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 395 | const cleartext_fragment_buf = cleartext_buf[cleartext_fragment_end..]; | 393 | const cleartext_fragment_buf = cleartext_buf[cleartext_fragment_end..]; |
| 396 | if (message_len > cleartext_fragment_buf.len) return error.TlsRecordOverflow; | 394 | if (message_len > cleartext_fragment_buf.len) return error.TlsRecordOverflow; |
| 397 | const cleartext = cleartext_fragment_buf[0..message_len]; | 395 | const cleartext = cleartext_fragment_buf[0..message_len]; |
| 398 | const ad = std.mem.toBytes(big(read_seq)) ++ | 396 | const ad = mem.toBytes(big(read_seq)) ++ |
| 399 | record_header[0 .. 1 + 2] ++ | 397 | record_header[0 .. 1 + 2] ++ |
| 400 | std.mem.toBytes(big(message_len)); | 398 | mem.toBytes(big(message_len)); |
| 401 | const record_iv = record_decoder.array(P.record_iv_length).*; | 399 | const record_iv = record_decoder.array(P.record_iv_length).*; |
| 402 | const masked_read_seq = read_seq & | 400 | const masked_read_seq = read_seq & |
| 403 | comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length); | 401 | comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length); |
| ... | @@ -738,7 +736,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client | ... | @@ -738,7 +736,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 738 | &.{ "server finished", &p.transcript_hash.finalResult() }, | 736 | &.{ "server finished", &p.transcript_hash.finalResult() }, |
| 739 | P.verify_data_length, | 737 | P.verify_data_length, |
| 740 | ), | 738 | ), |
| 741 | .app_cipher = std.mem.bytesToValue(P.Tls_1_2, &key_block), | 739 | .app_cipher = mem.bytesToValue(P.Tls_1_2, &key_block), |
| 742 | } }; | 740 | } }; |
| 743 | const pv = &p.version.tls_1_2; | 741 | const pv = &p.version.tls_1_2; |
| 744 | const nonce: [P.AEAD.nonce_length]u8 = nonce: { | 742 | const nonce: [P.AEAD.nonce_length]u8 = nonce: { |
| ... | @@ -756,7 +754,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client | ... | @@ -756,7 +754,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 756 | client_verify_cleartext.len ..][0..client_verify_cleartext.len], | 754 | client_verify_cleartext.len ..][0..client_verify_cleartext.len], |
| 757 | client_verify_msg[client_verify_msg.len - P.mac_length ..][0..P.mac_length], | 755 | client_verify_msg[client_verify_msg.len - P.mac_length ..][0..P.mac_length], |
| 758 | &client_verify_cleartext, | 756 | &client_verify_cleartext, |
| 759 | std.mem.toBytes(big(write_seq)) ++ client_verify_msg[0 .. 1 + 2] ++ int(u16, client_verify_cleartext.len), | 757 | mem.toBytes(big(write_seq)) ++ client_verify_msg[0 .. 1 + 2] ++ int(u16, client_verify_cleartext.len), |
| 760 | nonce, | 758 | nonce, |
| 761 | pv.app_cipher.client_write_key, | 759 | pv.app_cipher.client_write_key, |
| 762 | ); | 760 | ); |
| ... | @@ -873,7 +871,10 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client | ... | @@ -873,7 +871,10 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 873 | .input = input, | 871 | .input = input, |
| 874 | .reader = .{ | 872 | .reader = .{ |
| 875 | .buffer = options.read_buffer, | 873 | .buffer = options.read_buffer, |
| 876 | .vtable = &.{ .stream = stream }, | 874 | .vtable = &.{ |
| 875 | .stream = stream, | ||
| 876 | .readVec = readVec, | ||
| 877 | }, | ||
| 877 | .seek = 0, | 878 | .seek = 0, |
| 878 | .end = 0, | 879 | .end = 0, |
| 879 | }, | 880 | }, |
| ... | @@ -1017,7 +1018,7 @@ fn prepareCiphertextRecord( | ... | @@ -1017,7 +1018,7 @@ fn prepareCiphertextRecord( |
| 1017 | const nonce = nonce: { | 1018 | const nonce = nonce: { |
| 1018 | const V = @Vector(P.AEAD.nonce_length, u8); | 1019 | const V = @Vector(P.AEAD.nonce_length, u8); |
| 1019 | const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8); | 1020 | const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8); |
| 1020 | const operand: V = pad ++ std.mem.toBytes(big(c.write_seq)); | 1021 | const operand: V = pad ++ mem.toBytes(big(c.write_seq)); |
| 1021 | break :nonce @as(V, pv.client_iv) ^ operand; | 1022 | break :nonce @as(V, pv.client_iv) ^ operand; |
| 1022 | }; | 1023 | }; |
| 1023 | P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, pv.client_key); | 1024 | P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, pv.client_key); |
| ... | @@ -1048,7 +1049,7 @@ fn prepareCiphertextRecord( | ... | @@ -1048,7 +1049,7 @@ fn prepareCiphertextRecord( |
| 1048 | record_header.* = .{@intFromEnum(inner_content_type)} ++ | 1049 | record_header.* = .{@intFromEnum(inner_content_type)} ++ |
| 1049 | int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ | 1050 | int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ |
| 1050 | int(u16, P.record_iv_length + message_len + P.mac_length); | 1051 | int(u16, P.record_iv_length + message_len + P.mac_length); |
| 1051 | const ad = std.mem.toBytes(big(c.write_seq)) ++ record_header[0 .. 1 + 2] ++ int(u16, message_len); | 1052 | const ad = mem.toBytes(big(c.write_seq)) ++ record_header[0 .. 1 + 2] ++ int(u16, message_len); |
| 1052 | const record_iv = ciphertext_buf[ciphertext_end..][0..P.record_iv_length]; | 1053 | const record_iv = ciphertext_buf[ciphertext_end..][0..P.record_iv_length]; |
| 1053 | ciphertext_end += P.record_iv_length; | 1054 | ciphertext_end += P.record_iv_length; |
| 1054 | const nonce: [P.AEAD.nonce_length]u8 = nonce: { | 1055 | const nonce: [P.AEAD.nonce_length]u8 = nonce: { |
| ... | @@ -1076,7 +1077,22 @@ pub fn eof(c: Client) bool { | ... | @@ -1076,7 +1077,22 @@ pub fn eof(c: Client) bool { |
| 1076 | } | 1077 | } |
| 1077 | 1078 | ||
| 1078 | fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize { | 1079 | fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize { |
| 1080 | // This function writes exclusively to the buffer. | ||
| 1081 | _ = w; | ||
| 1082 | _ = limit; | ||
| 1083 | const c: *Client = @alignCast(@fieldParentPtr("reader", r)); | ||
| 1084 | return readIndirect(c); | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | fn readVec(r: *Reader, data: [][]u8) Reader.Error!usize { | ||
| 1088 | // This function writes exclusively to the buffer. | ||
| 1089 | _ = data; | ||
| 1079 | const c: *Client = @alignCast(@fieldParentPtr("reader", r)); | 1090 | const c: *Client = @alignCast(@fieldParentPtr("reader", r)); |
| 1091 | return readIndirect(c); | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | fn readIndirect(c: *Client) Reader.Error!usize { | ||
| 1095 | const r = &c.reader; | ||
| 1080 | if (c.eof()) return error.EndOfStream; | 1096 | if (c.eof()) return error.EndOfStream; |
| 1081 | const input = c.input; | 1097 | const input = c.input; |
| 1082 | // If at least one full encrypted record is not buffered, read once. | 1098 | // If at least one full encrypted record is not buffered, read once. |
| ... | @@ -1108,8 +1124,13 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize | ... | @@ -1108,8 +1124,13 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize |
| 1108 | if (record_end > input.buffered().len) return 0; | 1124 | if (record_end > input.buffered().len) return 0; |
| 1109 | } | 1125 | } |
| 1110 | 1126 | ||
| 1111 | var cleartext_stack_buffer: [max_ciphertext_len]u8 = undefined; | 1127 | if (r.seek == r.end) { |
| 1112 | const cleartext, const inner_ct: tls.ContentType = cleartext: switch (c.application_cipher) { | 1128 | r.seek = 0; |
| 1129 | r.end = 0; | ||
| 1130 | } | ||
| 1131 | const cleartext_buffer = r.buffer[r.end..]; | ||
| 1132 | |||
| 1133 | const cleartext_len, const inner_ct: tls.ContentType = cleartext: switch (c.application_cipher) { | ||
| 1113 | inline else => |*p| switch (c.tls_version) { | 1134 | inline else => |*p| switch (c.tls_version) { |
| 1114 | .tls_1_3 => { | 1135 | .tls_1_3 => { |
| 1115 | const pv = &p.tls_1_3; | 1136 | const pv = &p.tls_1_3; |
| ... | @@ -1121,23 +1142,24 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize | ... | @@ -1121,23 +1142,24 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize |
| 1121 | const nonce = nonce: { | 1142 | const nonce = nonce: { |
| 1122 | const V = @Vector(P.AEAD.nonce_length, u8); | 1143 | const V = @Vector(P.AEAD.nonce_length, u8); |
| 1123 | const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8); | 1144 | const pad = [1]u8{0} ** (P.AEAD.nonce_length - 8); |
| 1124 | const operand: V = pad ++ std.mem.toBytes(big(c.read_seq)); | 1145 | const operand: V = pad ++ mem.toBytes(big(c.read_seq)); |
| 1125 | break :nonce @as(V, pv.server_iv) ^ operand; | 1146 | break :nonce @as(V, pv.server_iv) ^ operand; |
| 1126 | }; | 1147 | }; |
| 1127 | const cleartext = cleartext_stack_buffer[0..ciphertext.len]; | 1148 | const cleartext = cleartext_buffer[0..ciphertext.len]; |
| 1128 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_key) catch | 1149 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_key) catch |
| 1129 | return failRead(c, error.TlsBadRecordMac); | 1150 | return failRead(c, error.TlsBadRecordMac); |
| 1151 | // TODO use scalar, non-slice version | ||
| 1130 | const msg = mem.trimRight(u8, cleartext, "\x00"); | 1152 | const msg = mem.trimRight(u8, cleartext, "\x00"); |
| 1131 | break :cleartext .{ msg[0 .. msg.len - 1], @enumFromInt(msg[msg.len - 1]) }; | 1153 | break :cleartext .{ msg.len - 1, @enumFromInt(msg[msg.len - 1]) }; |
| 1132 | }, | 1154 | }, |
| 1133 | .tls_1_2 => { | 1155 | .tls_1_2 => { |
| 1134 | const pv = &p.tls_1_2; | 1156 | const pv = &p.tls_1_2; |
| 1135 | const P = @TypeOf(p.*); | 1157 | const P = @TypeOf(p.*); |
| 1136 | const message_len: u16 = record_len - P.record_iv_length - P.mac_length; | 1158 | const message_len: u16 = record_len - P.record_iv_length - P.mac_length; |
| 1137 | const ad_header = input.take(tls.record_header_len) catch unreachable; // already peeked | 1159 | const ad_header = input.take(tls.record_header_len) catch unreachable; // already peeked |
| 1138 | const ad = std.mem.toBytes(big(c.read_seq)) ++ | 1160 | const ad = mem.toBytes(big(c.read_seq)) ++ |
| 1139 | ad_header[0 .. 1 + 2] ++ | 1161 | ad_header[0 .. 1 + 2] ++ |
| 1140 | std.mem.toBytes(big(message_len)); | 1162 | mem.toBytes(big(message_len)); |
| 1141 | const record_iv = (input.takeArray(P.record_iv_length) catch unreachable).*; // already peeked | 1163 | const record_iv = (input.takeArray(P.record_iv_length) catch unreachable).*; // already peeked |
| 1142 | const masked_read_seq = c.read_seq & | 1164 | const masked_read_seq = c.read_seq & |
| 1143 | comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length); | 1165 | comptime std.math.shl(u64, std.math.maxInt(u64), 8 * P.record_iv_length); |
| ... | @@ -1149,14 +1171,15 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize | ... | @@ -1149,14 +1171,15 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize |
| 1149 | }; | 1171 | }; |
| 1150 | const ciphertext = input.take(message_len) catch unreachable; // already peeked | 1172 | const ciphertext = input.take(message_len) catch unreachable; // already peeked |
| 1151 | const auth_tag = (input.takeArray(P.mac_length) catch unreachable).*; // already peeked | 1173 | const auth_tag = (input.takeArray(P.mac_length) catch unreachable).*; // already peeked |
| 1152 | const cleartext = cleartext_stack_buffer[0..ciphertext.len]; | 1174 | const cleartext = cleartext_buffer[0..ciphertext.len]; |
| 1153 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_write_key) catch | 1175 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_write_key) catch |
| 1154 | return failRead(c, error.TlsBadRecordMac); | 1176 | return failRead(c, error.TlsBadRecordMac); |
| 1155 | break :cleartext .{ cleartext, ct }; | 1177 | break :cleartext .{ cleartext.len, ct }; |
| 1156 | }, | 1178 | }, |
| 1157 | else => unreachable, | 1179 | else => unreachable, |
| 1158 | }, | 1180 | }, |
| 1159 | }; | 1181 | }; |
| 1182 | const cleartext = cleartext_buffer[0..cleartext_len]; | ||
| 1160 | c.read_seq = std.math.add(u64, c.read_seq, 1) catch return failRead(c, error.TlsSequenceOverflow); | 1183 | c.read_seq = std.math.add(u64, c.read_seq, 1) catch return failRead(c, error.TlsSequenceOverflow); |
| 1161 | switch (inner_ct) { | 1184 | switch (inner_ct) { |
| 1162 | .alert => { | 1185 | .alert => { |
| ... | @@ -1245,9 +1268,8 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize | ... | @@ -1245,9 +1268,8 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize |
| 1245 | return 0; | 1268 | return 0; |
| 1246 | }, | 1269 | }, |
| 1247 | .application_data => { | 1270 | .application_data => { |
| 1248 | if (@intFromEnum(limit) < cleartext.len) return failRead(c, error.OutputBufferUndersize); | 1271 | r.end += cleartext.len; |
| 1249 | try w.writeAll(cleartext); | 1272 | return 0; |
| 1250 | return cleartext.len; | ||
| 1251 | }, | 1273 | }, |
| 1252 | else => return failRead(c, error.TlsUnexpectedMessage), | 1274 | else => return failRead(c, error.TlsUnexpectedMessage), |
| 1253 | } | 1275 | } |
lib/std/http.zig+35-23| ... | @@ -292,6 +292,14 @@ pub const ContentEncoding = enum { | ... | @@ -292,6 +292,14 @@ pub const ContentEncoding = enum { |
| 292 | }); | 292 | }); |
| 293 | return map.get(s); | 293 | return map.get(s); |
| 294 | } | 294 | } |
| 295 | |||
| 296 | pub fn minBufferCapacity(ce: ContentEncoding) usize { | ||
| 297 | return switch (ce) { | ||
| 298 | .zstd => std.compress.zstd.default_window_len, | ||
| 299 | .gzip, .deflate => std.compress.flate.max_window_len, | ||
| 300 | .compress, .identity => 0, | ||
| 301 | }; | ||
| 302 | } | ||
| 295 | }; | 303 | }; |
| 296 | 304 | ||
| 297 | pub const Connection = enum { | 305 | pub const Connection = enum { |
| ... | @@ -412,7 +420,7 @@ pub const Reader = struct { | ... | @@ -412,7 +420,7 @@ pub const Reader = struct { |
| 412 | /// * `interfaceDecompressing` | 420 | /// * `interfaceDecompressing` |
| 413 | pub fn bodyReader( | 421 | pub fn bodyReader( |
| 414 | reader: *Reader, | 422 | reader: *Reader, |
| 415 | buffer: []u8, | 423 | transfer_buffer: []u8, |
| 416 | transfer_encoding: TransferEncoding, | 424 | transfer_encoding: TransferEncoding, |
| 417 | content_length: ?u64, | 425 | content_length: ?u64, |
| 418 | ) *std.Io.Reader { | 426 | ) *std.Io.Reader { |
| ... | @@ -421,7 +429,7 @@ pub const Reader = struct { | ... | @@ -421,7 +429,7 @@ pub const Reader = struct { |
| 421 | .chunked => { | 429 | .chunked => { |
| 422 | reader.state = .{ .body_remaining_chunk_len = .head }; | 430 | reader.state = .{ .body_remaining_chunk_len = .head }; |
| 423 | reader.interface = .{ | 431 | reader.interface = .{ |
| 424 | .buffer = buffer, | 432 | .buffer = transfer_buffer, |
| 425 | .seek = 0, | 433 | .seek = 0, |
| 426 | .end = 0, | 434 | .end = 0, |
| 427 | .vtable = &.{ | 435 | .vtable = &.{ |
| ... | @@ -435,7 +443,7 @@ pub const Reader = struct { | ... | @@ -435,7 +443,7 @@ pub const Reader = struct { |
| 435 | if (content_length) |len| { | 443 | if (content_length) |len| { |
| 436 | reader.state = .{ .body_remaining_content_length = len }; | 444 | reader.state = .{ .body_remaining_content_length = len }; |
| 437 | reader.interface = .{ | 445 | reader.interface = .{ |
| 438 | .buffer = buffer, | 446 | .buffer = transfer_buffer, |
| 439 | .seek = 0, | 447 | .seek = 0, |
| 440 | .end = 0, | 448 | .end = 0, |
| 441 | .vtable = &.{ | 449 | .vtable = &.{ |
| ... | @@ -460,11 +468,12 @@ pub const Reader = struct { | ... | @@ -460,11 +468,12 @@ pub const Reader = struct { |
| 460 | /// * `interface` | 468 | /// * `interface` |
| 461 | pub fn bodyReaderDecompressing( | 469 | pub fn bodyReaderDecompressing( |
| 462 | reader: *Reader, | 470 | reader: *Reader, |
| 471 | transfer_buffer: []u8, | ||
| 463 | transfer_encoding: TransferEncoding, | 472 | transfer_encoding: TransferEncoding, |
| 464 | content_length: ?u64, | 473 | content_length: ?u64, |
| 465 | content_encoding: ContentEncoding, | 474 | content_encoding: ContentEncoding, |
| 466 | decompressor: *Decompressor, | 475 | decompress: *Decompress, |
| 467 | decompression_buffer: []u8, | 476 | decompress_buffer: []u8, |
| 468 | ) *std.Io.Reader { | 477 | ) *std.Io.Reader { |
| 469 | if (transfer_encoding == .none and content_length == null) { | 478 | if (transfer_encoding == .none and content_length == null) { |
| 470 | assert(reader.state == .received_head); | 479 | assert(reader.state == .received_head); |
| ... | @@ -474,22 +483,22 @@ pub const Reader = struct { | ... | @@ -474,22 +483,22 @@ pub const Reader = struct { |
| 474 | return reader.in; | 483 | return reader.in; |
| 475 | }, | 484 | }, |
| 476 | .deflate => { | 485 | .deflate => { |
| 477 | decompressor.* = .{ .flate = .init(reader.in, .zlib, decompression_buffer) }; | 486 | decompress.* = .{ .flate = .init(reader.in, .zlib, decompress_buffer) }; |
| 478 | return &decompressor.flate.reader; | 487 | return &decompress.flate.reader; |
| 479 | }, | 488 | }, |
| 480 | .gzip => { | 489 | .gzip => { |
| 481 | decompressor.* = .{ .flate = .init(reader.in, .gzip, decompression_buffer) }; | 490 | decompress.* = .{ .flate = .init(reader.in, .gzip, decompress_buffer) }; |
| 482 | return &decompressor.flate.reader; | 491 | return &decompress.flate.reader; |
| 483 | }, | 492 | }, |
| 484 | .zstd => { | 493 | .zstd => { |
| 485 | decompressor.* = .{ .zstd = .init(reader.in, decompression_buffer, .{ .verify_checksum = false }) }; | 494 | decompress.* = .{ .zstd = .init(reader.in, decompress_buffer, .{ .verify_checksum = false }) }; |
| 486 | return &decompressor.zstd.reader; | 495 | return &decompress.zstd.reader; |
| 487 | }, | 496 | }, |
| 488 | .compress => unreachable, | 497 | .compress => unreachable, |
| 489 | } | 498 | } |
| 490 | } | 499 | } |
| 491 | const transfer_reader = bodyReader(reader, &.{}, transfer_encoding, content_length); | 500 | const transfer_reader = bodyReader(reader, transfer_buffer, transfer_encoding, content_length); |
| 492 | return decompressor.init(transfer_reader, decompression_buffer, content_encoding); | 501 | return decompress.init(transfer_reader, decompress_buffer, content_encoding); |
| 493 | } | 502 | } |
| 494 | 503 | ||
| 495 | fn contentLengthStream( | 504 | fn contentLengthStream( |
| ... | @@ -691,33 +700,33 @@ pub const Reader = struct { | ... | @@ -691,33 +700,33 @@ pub const Reader = struct { |
| 691 | } | 700 | } |
| 692 | }; | 701 | }; |
| 693 | 702 | ||
| 694 | pub const Decompressor = union(enum) { | 703 | pub const Decompress = union(enum) { |
| 695 | flate: std.compress.flate.Decompress, | 704 | flate: std.compress.flate.Decompress, |
| 696 | zstd: std.compress.zstd.Decompress, | 705 | zstd: std.compress.zstd.Decompress, |
| 697 | none: *std.Io.Reader, | 706 | none: *std.Io.Reader, |
| 698 | 707 | ||
| 699 | pub fn init( | 708 | pub fn init( |
| 700 | decompressor: *Decompressor, | 709 | decompress: *Decompress, |
| 701 | transfer_reader: *std.Io.Reader, | 710 | transfer_reader: *std.Io.Reader, |
| 702 | buffer: []u8, | 711 | buffer: []u8, |
| 703 | content_encoding: ContentEncoding, | 712 | content_encoding: ContentEncoding, |
| 704 | ) *std.Io.Reader { | 713 | ) *std.Io.Reader { |
| 705 | switch (content_encoding) { | 714 | switch (content_encoding) { |
| 706 | .identity => { | 715 | .identity => { |
| 707 | decompressor.* = .{ .none = transfer_reader }; | 716 | decompress.* = .{ .none = transfer_reader }; |
| 708 | return transfer_reader; | 717 | return transfer_reader; |
| 709 | }, | 718 | }, |
| 710 | .deflate => { | 719 | .deflate => { |
| 711 | decompressor.* = .{ .flate = .init(transfer_reader, .zlib, buffer) }; | 720 | decompress.* = .{ .flate = .init(transfer_reader, .zlib, buffer) }; |
| 712 | return &decompressor.flate.reader; | 721 | return &decompress.flate.reader; |
| 713 | }, | 722 | }, |
| 714 | .gzip => { | 723 | .gzip => { |
| 715 | decompressor.* = .{ .flate = .init(transfer_reader, .gzip, buffer) }; | 724 | decompress.* = .{ .flate = .init(transfer_reader, .gzip, buffer) }; |
| 716 | return &decompressor.flate.reader; | 725 | return &decompress.flate.reader; |
| 717 | }, | 726 | }, |
| 718 | .zstd => { | 727 | .zstd => { |
| 719 | decompressor.* = .{ .zstd = .init(transfer_reader, buffer, .{ .verify_checksum = false }) }; | 728 | decompress.* = .{ .zstd = .init(transfer_reader, buffer, .{ .verify_checksum = false }) }; |
| 720 | return &decompressor.zstd.reader; | 729 | return &decompress.zstd.reader; |
| 721 | }, | 730 | }, |
| 722 | .compress => unreachable, | 731 | .compress => unreachable, |
| 723 | } | 732 | } |
| ... | @@ -794,7 +803,7 @@ pub const BodyWriter = struct { | ... | @@ -794,7 +803,7 @@ pub const BodyWriter = struct { |
| 794 | } | 803 | } |
| 795 | 804 | ||
| 796 | /// When using content-length, asserts that the amount of data sent matches | 805 | /// When using content-length, asserts that the amount of data sent matches |
| 797 | /// the value sent in the header, then flushes. | 806 | /// the value sent in the header, then flushes `http_protocol_output`. |
| 798 | /// | 807 | /// |
| 799 | /// When using transfer-encoding: chunked, writes the end-of-stream message | 808 | /// When using transfer-encoding: chunked, writes the end-of-stream message |
| 800 | /// with empty trailers, then flushes the stream to the system. Asserts any | 809 | /// with empty trailers, then flushes the stream to the system. Asserts any |
| ... | @@ -818,10 +827,13 @@ pub const BodyWriter = struct { | ... | @@ -818,10 +827,13 @@ pub const BodyWriter = struct { |
| 818 | /// | 827 | /// |
| 819 | /// Respects the value of `isEliding` to omit all data after the headers. | 828 | /// Respects the value of `isEliding` to omit all data after the headers. |
| 820 | /// | 829 | /// |
| 830 | /// Does not flush `http_protocol_output`, but does flush `writer`. | ||
| 831 | /// | ||
| 821 | /// See also: | 832 | /// See also: |
| 822 | /// * `end` | 833 | /// * `end` |
| 823 | /// * `endChunked` | 834 | /// * `endChunked` |
| 824 | pub fn endUnflushed(w: *BodyWriter) Error!void { | 835 | pub fn endUnflushed(w: *BodyWriter) Error!void { |
| 836 | try w.writer.flush(); | ||
| 825 | switch (w.state) { | 837 | switch (w.state) { |
| 826 | .end => unreachable, | 838 | .end => unreachable, |
| 827 | .content_length => |len| { | 839 | .content_length => |len| { |
lib/std/http/Client.zig+22-27| ... | @@ -13,8 +13,8 @@ const net = std.net; | ... | @@ -13,8 +13,8 @@ const net = std.net; |
| 13 | const Uri = std.Uri; | 13 | const Uri = std.Uri; |
| 14 | const Allocator = mem.Allocator; | 14 | const Allocator = mem.Allocator; |
| 15 | const assert = std.debug.assert; | 15 | const assert = std.debug.assert; |
| 16 | const Writer = std.io.Writer; | 16 | const Writer = std.Io.Writer; |
| 17 | const Reader = std.io.Reader; | 17 | const Reader = std.Io.Reader; |
| 18 | 18 | ||
| 19 | const Client = @This(); | 19 | const Client = @This(); |
| 20 | 20 | ||
| ... | @@ -704,12 +704,12 @@ pub const Response = struct { | ... | @@ -704,12 +704,12 @@ pub const Response = struct { |
| 704 | /// | 704 | /// |
| 705 | /// See also: | 705 | /// See also: |
| 706 | /// * `readerDecompressing` | 706 | /// * `readerDecompressing` |
| 707 | pub fn reader(response: *Response, buffer: []u8) *Reader { | 707 | pub fn reader(response: *Response, transfer_buffer: []u8) *Reader { |
| 708 | response.head.invalidateStrings(); | 708 | response.head.invalidateStrings(); |
| 709 | const req = response.request; | 709 | const req = response.request; |
| 710 | if (!req.method.responseHasBody()) return .ending; | 710 | if (!req.method.responseHasBody()) return .ending; |
| 711 | const head = &response.head; | 711 | const head = &response.head; |
| 712 | return req.reader.bodyReader(buffer, head.transfer_encoding, head.content_length); | 712 | return req.reader.bodyReader(transfer_buffer, head.transfer_encoding, head.content_length); |
| 713 | } | 713 | } |
| 714 | 714 | ||
| 715 | /// If compressed body has been negotiated this will return decompressed bytes. | 715 | /// If compressed body has been negotiated this will return decompressed bytes. |
| ... | @@ -723,17 +723,19 @@ pub const Response = struct { | ... | @@ -723,17 +723,19 @@ pub const Response = struct { |
| 723 | /// * `reader` | 723 | /// * `reader` |
| 724 | pub fn readerDecompressing( | 724 | pub fn readerDecompressing( |
| 725 | response: *Response, | 725 | response: *Response, |
| 726 | decompressor: *http.Decompressor, | 726 | transfer_buffer: []u8, |
| 727 | decompression_buffer: []u8, | 727 | decompress: *http.Decompress, |
| 728 | decompress_buffer: []u8, | ||
| 728 | ) *Reader { | 729 | ) *Reader { |
| 729 | response.head.invalidateStrings(); | 730 | response.head.invalidateStrings(); |
| 730 | const head = &response.head; | 731 | const head = &response.head; |
| 731 | return response.request.reader.bodyReaderDecompressing( | 732 | return response.request.reader.bodyReaderDecompressing( |
| 733 | transfer_buffer, | ||
| 732 | head.transfer_encoding, | 734 | head.transfer_encoding, |
| 733 | head.content_length, | 735 | head.content_length, |
| 734 | head.content_encoding, | 736 | head.content_encoding, |
| 735 | decompressor, | 737 | decompress, |
| 736 | decompression_buffer, | 738 | decompress_buffer, |
| 737 | ); | 739 | ); |
| 738 | } | 740 | } |
| 739 | 741 | ||
| ... | @@ -1322,7 +1324,7 @@ pub const basic_authorization = struct { | ... | @@ -1322,7 +1324,7 @@ pub const basic_authorization = struct { |
| 1322 | const user: Uri.Component = uri.user orelse .empty; | 1324 | const user: Uri.Component = uri.user orelse .empty; |
| 1323 | const password: Uri.Component = uri.password orelse .empty; | 1325 | const password: Uri.Component = uri.password orelse .empty; |
| 1324 | 1326 | ||
| 1325 | var dw: std.io.Writer.Discarding = .init(&.{}); | 1327 | var dw: Writer.Discarding = .init(&.{}); |
| 1326 | user.formatUser(&dw.writer) catch unreachable; // discarding | 1328 | user.formatUser(&dw.writer) catch unreachable; // discarding |
| 1327 | const user_len = dw.count + dw.writer.end; | 1329 | const user_len = dw.count + dw.writer.end; |
| 1328 | 1330 | ||
| ... | @@ -1696,8 +1698,8 @@ pub const FetchOptions = struct { | ... | @@ -1696,8 +1698,8 @@ pub const FetchOptions = struct { |
| 1696 | /// `null` means it will be heap-allocated. | 1698 | /// `null` means it will be heap-allocated. |
| 1697 | decompress_buffer: ?[]u8 = null, | 1699 | decompress_buffer: ?[]u8 = null, |
| 1698 | redirect_behavior: ?Request.RedirectBehavior = null, | 1700 | redirect_behavior: ?Request.RedirectBehavior = null, |
| 1699 | /// If the server sends a body, it will be stored here. | 1701 | /// If the server sends a body, it will be written here. |
| 1700 | response_storage: ?ResponseStorage = null, | 1702 | response_writer: ?*Writer = null, |
| 1701 | 1703 | ||
| 1702 | location: Location, | 1704 | location: Location, |
| 1703 | method: ?http.Method = null, | 1705 | method: ?http.Method = null, |
| ... | @@ -1725,7 +1727,7 @@ pub const FetchOptions = struct { | ... | @@ -1725,7 +1727,7 @@ pub const FetchOptions = struct { |
| 1725 | list: *std.ArrayListUnmanaged(u8), | 1727 | list: *std.ArrayListUnmanaged(u8), |
| 1726 | /// If null then only the existing capacity will be used. | 1728 | /// If null then only the existing capacity will be used. |
| 1727 | allocator: ?Allocator = null, | 1729 | allocator: ?Allocator = null, |
| 1728 | append_limit: std.io.Limit = .unlimited, | 1730 | append_limit: std.Io.Limit = .unlimited, |
| 1729 | }; | 1731 | }; |
| 1730 | }; | 1732 | }; |
| 1731 | 1733 | ||
| ... | @@ -1778,7 +1780,7 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult { | ... | @@ -1778,7 +1780,7 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult { |
| 1778 | 1780 | ||
| 1779 | var response = try req.receiveHead(redirect_buffer); | 1781 | var response = try req.receiveHead(redirect_buffer); |
| 1780 | 1782 | ||
| 1781 | const storage = options.response_storage orelse { | 1783 | const response_writer = options.response_writer orelse { |
| 1782 | const reader = response.reader(&.{}); | 1784 | const reader = response.reader(&.{}); |
| 1783 | _ = reader.discardRemaining() catch |err| switch (err) { | 1785 | _ = reader.discardRemaining() catch |err| switch (err) { |
| 1784 | error.ReadFailed => return response.bodyErr().?, | 1786 | error.ReadFailed => return response.bodyErr().?, |
| ... | @@ -1794,21 +1796,14 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult { | ... | @@ -1794,21 +1796,14 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult { |
| 1794 | }; | 1796 | }; |
| 1795 | defer if (options.decompress_buffer == null) client.allocator.free(decompress_buffer); | 1797 | defer if (options.decompress_buffer == null) client.allocator.free(decompress_buffer); |
| 1796 | 1798 | ||
| 1797 | var decompressor: http.Decompressor = undefined; | 1799 | var transfer_buffer: [64]u8 = undefined; |
| 1798 | const reader = response.readerDecompressing(&decompressor, decompress_buffer); | 1800 | var decompress: http.Decompress = undefined; |
| 1799 | const list = storage.list; | 1801 | const reader = response.readerDecompressing(&transfer_buffer, &decompress, decompress_buffer); |
| 1800 | 1802 | ||
| 1801 | if (storage.allocator) |allocator| { | 1803 | _ = reader.streamRemaining(response_writer) catch |err| switch (err) { |
| 1802 | reader.appendRemaining(allocator, null, list, storage.append_limit) catch |err| switch (err) { | 1804 | error.ReadFailed => return response.bodyErr().?, |
| 1803 | error.ReadFailed => return response.bodyErr().?, | 1805 | else => |e| return e, |
| 1804 | else => |e| return e, | 1806 | }; |
| 1805 | }; | ||
| 1806 | } else { | ||
| 1807 | const buf = storage.append_limit.slice(list.unusedCapacitySlice()); | ||
| 1808 | list.items.len += reader.readSliceShort(buf) catch |err| switch (err) { | ||
| 1809 | error.ReadFailed => return response.bodyErr().?, | ||
| 1810 | }; | ||
| 1811 | } | ||
| 1812 | 1807 | ||
| 1813 | return .{ .status = response.head.status }; | 1808 | return .{ .status = response.head.status }; |
| 1814 | } | 1809 | } |
lib/std/http/test.zig+5-4| ... | @@ -1006,8 +1006,9 @@ fn echoTests(client: *http.Client, port: u16) !void { | ... | @@ -1006,8 +1006,9 @@ fn echoTests(client: *http.Client, port: u16) !void { |
| 1006 | const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}/echo-content#fetch", .{port}); | 1006 | const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}/echo-content#fetch", .{port}); |
| 1007 | defer gpa.free(location); | 1007 | defer gpa.free(location); |
| 1008 | 1008 | ||
| 1009 | var body: std.ArrayListUnmanaged(u8) = .empty; | 1009 | var body: std.Io.Writer.Allocating = .init(gpa); |
| 1010 | defer body.deinit(gpa); | 1010 | defer body.deinit(); |
| 1011 | try body.ensureUnusedCapacity(64); | ||
| 1011 | 1012 | ||
| 1012 | const res = try client.fetch(.{ | 1013 | const res = try client.fetch(.{ |
| 1013 | .location = .{ .url = location }, | 1014 | .location = .{ .url = location }, |
| ... | @@ -1016,10 +1017,10 @@ fn echoTests(client: *http.Client, port: u16) !void { | ... | @@ -1016,10 +1017,10 @@ fn echoTests(client: *http.Client, port: u16) !void { |
| 1016 | .extra_headers = &.{ | 1017 | .extra_headers = &.{ |
| 1017 | .{ .name = "content-type", .value = "text/plain" }, | 1018 | .{ .name = "content-type", .value = "text/plain" }, |
| 1018 | }, | 1019 | }, |
| 1019 | .response_storage = .{ .allocator = gpa, .list = &body }, | 1020 | .response_writer = &body.writer, |
| 1020 | }); | 1021 | }); |
| 1021 | try expectEqual(.ok, res.status); | 1022 | try expectEqual(.ok, res.status); |
| 1022 | try expectEqualStrings("Hello, World!\n", body.items); | 1023 | try expectEqualStrings("Hello, World!\n", body.getWritten()); |
| 1023 | } | 1024 | } |
| 1024 | 1025 | ||
| 1025 | { // expect: 100-continue | 1026 | { // expect: 100-continue |
src/Package/Fetch.zig+24-18| ... | @@ -883,7 +883,9 @@ const Resource = union(enum) { | ... | @@ -883,7 +883,9 @@ const Resource = union(enum) { |
| 883 | const HttpRequest = struct { | 883 | const HttpRequest = struct { |
| 884 | request: std.http.Client.Request, | 884 | request: std.http.Client.Request, |
| 885 | response: std.http.Client.Response, | 885 | response: std.http.Client.Response, |
| 886 | buffer: []u8, | 886 | transfer_buffer: []u8, |
| 887 | decompress: std.http.Decompress, | ||
| 888 | decompress_buffer: []u8, | ||
| 887 | }; | 889 | }; |
| 888 | 890 | ||
| 889 | fn deinit(resource: *Resource) void { | 891 | fn deinit(resource: *Resource) void { |
| ... | @@ -892,7 +894,6 @@ const Resource = union(enum) { | ... | @@ -892,7 +894,6 @@ const Resource = union(enum) { |
| 892 | .http_request => |*http_request| http_request.request.deinit(), | 894 | .http_request => |*http_request| http_request.request.deinit(), |
| 893 | .git => |*git_resource| { | 895 | .git => |*git_resource| { |
| 894 | git_resource.fetch_stream.deinit(); | 896 | git_resource.fetch_stream.deinit(); |
| 895 | git_resource.session.deinit(); | ||
| 896 | }, | 897 | }, |
| 897 | .dir => |*dir| dir.close(), | 898 | .dir => |*dir| dir.close(), |
| 898 | } | 899 | } |
| ... | @@ -902,7 +903,11 @@ const Resource = union(enum) { | ... | @@ -902,7 +903,11 @@ const Resource = union(enum) { |
| 902 | fn reader(resource: *Resource) *std.Io.Reader { | 903 | fn reader(resource: *Resource) *std.Io.Reader { |
| 903 | return switch (resource.*) { | 904 | return switch (resource.*) { |
| 904 | .file => |*file_reader| return &file_reader.interface, | 905 | .file => |*file_reader| return &file_reader.interface, |
| 905 | .http_request => |*http_request| return http_request.response.reader(http_request.buffer), | 906 | .http_request => |*http_request| return http_request.response.readerDecompressing( |
| 907 | http_request.transfer_buffer, | ||
| 908 | &http_request.decompress, | ||
| 909 | http_request.decompress_buffer, | ||
| 910 | ), | ||
| 906 | .git => |*g| return &g.fetch_stream.reader, | 911 | .git => |*g| return &g.fetch_stream.reader, |
| 907 | .dir => unreachable, | 912 | .dir => unreachable, |
| 908 | }; | 913 | }; |
| ... | @@ -971,7 +976,6 @@ const FileType = enum { | ... | @@ -971,7 +976,6 @@ const FileType = enum { |
| 971 | const init_resource_buffer_size = git.Packet.max_data_length; | 976 | const init_resource_buffer_size = git.Packet.max_data_length; |
| 972 | 977 | ||
| 973 | fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u8) RunError!void { | 978 | fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u8) RunError!void { |
| 974 | const gpa = f.arena.child_allocator; | ||
| 975 | const arena = f.arena.allocator(); | 979 | const arena = f.arena.allocator(); |
| 976 | const eb = &f.error_bundle; | 980 | const eb = &f.error_bundle; |
| 977 | 981 | ||
| ... | @@ -995,7 +999,9 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u | ... | @@ -995,7 +999,9 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u |
| 995 | .request = http_client.request(.GET, uri, .{}) catch |err| | 999 | .request = http_client.request(.GET, uri, .{}) catch |err| |
| 996 | return f.fail(f.location_tok, try eb.printString("unable to connect to server: {t}", .{err})), | 1000 | return f.fail(f.location_tok, try eb.printString("unable to connect to server: {t}", .{err})), |
| 997 | .response = undefined, | 1001 | .response = undefined, |
| 998 | .buffer = reader_buffer, | 1002 | .transfer_buffer = reader_buffer, |
| 1003 | .decompress_buffer = &.{}, | ||
| 1004 | .decompress = undefined, | ||
| 999 | } }; | 1005 | } }; |
| 1000 | const request = &resource.http_request.request; | 1006 | const request = &resource.http_request.request; |
| 1001 | errdefer request.deinit(); | 1007 | errdefer request.deinit(); |
| ... | @@ -1019,6 +1025,7 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u | ... | @@ -1019,6 +1025,7 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u |
| 1019 | .{ response.head.status, response.head.status.phrase() orelse "" }, | 1025 | .{ response.head.status, response.head.status.phrase() orelse "" }, |
| 1020 | )); | 1026 | )); |
| 1021 | 1027 | ||
| 1028 | resource.http_request.decompress_buffer = try arena.alloc(u8, response.head.content_encoding.minBufferCapacity()); | ||
| 1022 | return; | 1029 | return; |
| 1023 | } | 1030 | } |
| 1024 | 1031 | ||
| ... | @@ -1027,13 +1034,12 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u | ... | @@ -1027,13 +1034,12 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u |
| 1027 | { | 1034 | { |
| 1028 | var transport_uri = uri; | 1035 | var transport_uri = uri; |
| 1029 | transport_uri.scheme = uri.scheme["git+".len..]; | 1036 | transport_uri.scheme = uri.scheme["git+".len..]; |
| 1030 | var session = git.Session.init(gpa, http_client, transport_uri, reader_buffer) catch |err| { | 1037 | var session = git.Session.init(arena, http_client, transport_uri, reader_buffer) catch |err| { |
| 1031 | return f.fail(f.location_tok, try eb.printString( | 1038 | return f.fail( |
| 1032 | "unable to discover remote git server capabilities: {s}", | 1039 | f.location_tok, |
| 1033 | .{@errorName(err)}, | 1040 | try eb.printString("unable to discover remote git server capabilities: {t}", .{err}), |
| 1034 | )); | 1041 | ); |
| 1035 | }; | 1042 | }; |
| 1036 | errdefer session.deinit(); | ||
| 1037 | 1043 | ||
| 1038 | const want_oid = want_oid: { | 1044 | const want_oid = want_oid: { |
| 1039 | const want_ref = | 1045 | const want_ref = |
| ... | @@ -1086,17 +1092,17 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u | ... | @@ -1086,17 +1092,17 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u |
| 1086 | 1092 | ||
| 1087 | var want_oid_buf: [git.Oid.max_formatted_length]u8 = undefined; | 1093 | var want_oid_buf: [git.Oid.max_formatted_length]u8 = undefined; |
| 1088 | _ = std.fmt.bufPrint(&want_oid_buf, "{f}", .{want_oid}) catch unreachable; | 1094 | _ = std.fmt.bufPrint(&want_oid_buf, "{f}", .{want_oid}) catch unreachable; |
| 1089 | var fetch_stream: git.Session.FetchStream = undefined; | ||
| 1090 | session.fetch(&fetch_stream, &.{&want_oid_buf}, reader_buffer) catch |err| { | ||
| 1091 | return f.fail(f.location_tok, try eb.printString("unable to create fetch stream: {t}", .{err})); | ||
| 1092 | }; | ||
| 1093 | errdefer fetch_stream.deinit(); | ||
| 1094 | |||
| 1095 | resource.* = .{ .git = .{ | 1095 | resource.* = .{ .git = .{ |
| 1096 | .session = session, | 1096 | .session = session, |
| 1097 | .fetch_stream = fetch_stream, | 1097 | .fetch_stream = undefined, |
| 1098 | .want_oid = want_oid, | 1098 | .want_oid = want_oid, |
| 1099 | } }; | 1099 | } }; |
| 1100 | const fetch_stream = &resource.git.fetch_stream; | ||
| 1101 | session.fetch(fetch_stream, &.{&want_oid_buf}, reader_buffer) catch |err| { | ||
| 1102 | return f.fail(f.location_tok, try eb.printString("unable to create fetch stream: {t}", .{err})); | ||
| 1103 | }; | ||
| 1104 | errdefer fetch_stream.deinit(fetch_stream); | ||
| 1105 | |||
| 1100 | return; | 1106 | return; |
| 1101 | } | 1107 | } |
| 1102 | 1108 |
src/Package/Fetch/git.zig+40-57| ... | @@ -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(); |
| ... | @@ -1131,8 +1114,8 @@ pub const Session = struct { | ... | @@ -1131,8 +1114,8 @@ pub const Session = struct { |
| 1131 | } | 1114 | } |
| 1132 | const buf = limit.slice(try w.writableSliceGreedy(1)); | 1115 | const buf = limit.slice(try w.writableSliceGreedy(1)); |
| 1133 | const n = @min(buf.len, fs.remaining_len); | 1116 | const n = @min(buf.len, fs.remaining_len); |
| 1134 | @memcpy(buf[0..n], input.buffered()[0..n]); | 1117 | try input.readSliceAll(buf[0..n]); |
| 1135 | input.toss(n); | 1118 | w.advance(n); |
| 1136 | fs.remaining_len -= n; | 1119 | fs.remaining_len -= n; |
| 1137 | return n; | 1120 | return n; |
| 1138 | } | 1121 | } |