| 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 | 25 | /// |
| 26 | 26 | /// Returns the number of bytes written, which will be at minimum `0` and |
| 27 | 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 | |
| 29 | /// buffer capacity of `w`, a value whose minimum size is determined by the | |
| 30 | /// stream implementation. | |
| 28 | /// end of stream. | |
| 31 | 29 | /// |
| 32 | 30 | /// The reader's internal logical seek position moves forward in accordance |
| 33 | 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 | 61 | TlsUnexpectedMessage, |
| 62 | 62 | TlsIllegalParameter, |
| 63 | 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 | 66 | pub const SslKeyLog = struct { |
| ... | ... | @@ -372,7 +369,8 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 372 | 369 | }; |
| 373 | 370 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, record_header, nonce, pv.server_handshake_key) catch |
| 374 | 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 | 376 | read_seq += 1; |
| ... | ... | @@ -395,9 +393,9 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 395 | 393 | const cleartext_fragment_buf = cleartext_buf[cleartext_fragment_end..]; |
| 396 | 394 | if (message_len > cleartext_fragment_buf.len) return error.TlsRecordOverflow; |
| 397 | 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 | 397 | record_header[0 .. 1 + 2] ++ |
| 400 | std.mem.toBytes(big(message_len)); | |
| 398 | mem.toBytes(big(message_len)); | |
| 401 | 399 | const record_iv = record_decoder.array(P.record_iv_length).*; |
| 402 | 400 | const masked_read_seq = read_seq & |
| 403 | 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 | 736 | &.{ "server finished", &p.transcript_hash.finalResult() }, |
| 739 | 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 | 741 | const pv = &p.version.tls_1_2; |
| 744 | 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 | 754 | client_verify_cleartext.len ..][0..client_verify_cleartext.len], |
| 757 | 755 | client_verify_msg[client_verify_msg.len - P.mac_length ..][0..P.mac_length], |
| 758 | 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 | 758 | nonce, |
| 761 | 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 | 871 | .input = input, |
| 874 | 872 | .reader = .{ |
| 875 | 873 | .buffer = options.read_buffer, |
| 876 | .vtable = &.{ .stream = stream }, | |
| 874 | .vtable = &.{ | |
| 875 | .stream = stream, | |
| 876 | .readVec = readVec, | |
| 877 | }, | |
| 877 | 878 | .seek = 0, |
| 878 | 879 | .end = 0, |
| 879 | 880 | }, |
| ... | ... | @@ -1017,7 +1018,7 @@ fn prepareCiphertextRecord( |
| 1017 | 1018 | const nonce = nonce: { |
| 1018 | 1019 | const V = @Vector(P.AEAD.nonce_length, u8); |
| 1019 | 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 | 1022 | break :nonce @as(V, pv.client_iv) ^ operand; |
| 1022 | 1023 | }; |
| 1023 | 1024 | P.AEAD.encrypt(ciphertext, auth_tag, cleartext, ad, nonce, pv.client_key); |
| ... | ... | @@ -1048,7 +1049,7 @@ fn prepareCiphertextRecord( |
| 1048 | 1049 | record_header.* = .{@intFromEnum(inner_content_type)} ++ |
| 1049 | 1050 | int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++ |
| 1050 | 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 | 1053 | const record_iv = ciphertext_buf[ciphertext_end..][0..P.record_iv_length]; |
| 1053 | 1054 | ciphertext_end += P.record_iv_length; |
| 1054 | 1055 | const nonce: [P.AEAD.nonce_length]u8 = nonce: { |
| ... | ... | @@ -1076,7 +1077,22 @@ pub fn eof(c: Client) bool { |
| 1076 | 1077 | } |
| 1077 | 1078 | |
| 1078 | 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 | 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 | 1096 | if (c.eof()) return error.EndOfStream; |
| 1081 | 1097 | const input = c.input; |
| 1082 | 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 | 1124 | if (record_end > input.buffered().len) return 0; |
| 1109 | 1125 | } |
| 1110 | 1126 | |
| 1111 | var cleartext_stack_buffer: [max_ciphertext_len]u8 = undefined; | |
| 1112 | const cleartext, const inner_ct: tls.ContentType = cleartext: switch (c.application_cipher) { | |
| 1127 | if (r.seek == r.end) { | |
| 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 | 1134 | inline else => |*p| switch (c.tls_version) { |
| 1114 | 1135 | .tls_1_3 => { |
| 1115 | 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 | 1142 | const nonce = nonce: { |
| 1122 | 1143 | const V = @Vector(P.AEAD.nonce_length, u8); |
| 1123 | 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 | 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 | 1149 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_key) catch |
| 1129 | 1150 | return failRead(c, error.TlsBadRecordMac); |
| 1151 | // TODO use scalar, non-slice version | |
| 1130 | 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 | 1155 | .tls_1_2 => { |
| 1134 | 1156 | const pv = &p.tls_1_2; |
| 1135 | 1157 | const P = @TypeOf(p.*); |
| 1136 | 1158 | const message_len: u16 = record_len - P.record_iv_length - P.mac_length; |
| 1137 | 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 | 1161 | ad_header[0 .. 1 + 2] ++ |
| 1140 | std.mem.toBytes(big(message_len)); | |
| 1162 | mem.toBytes(big(message_len)); | |
| 1141 | 1163 | const record_iv = (input.takeArray(P.record_iv_length) catch unreachable).*; // already peeked |
| 1142 | 1164 | const masked_read_seq = c.read_seq & |
| 1143 | 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 | 1171 | }; |
| 1150 | 1172 | const ciphertext = input.take(message_len) catch unreachable; // already peeked |
| 1151 | 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 | 1175 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_write_key) catch |
| 1154 | 1176 | return failRead(c, error.TlsBadRecordMac); |
| 1155 | break :cleartext .{ cleartext, ct }; | |
| 1177 | break :cleartext .{ cleartext.len, ct }; | |
| 1156 | 1178 | }, |
| 1157 | 1179 | else => unreachable, |
| 1158 | 1180 | }, |
| 1159 | 1181 | }; |
| 1182 | const cleartext = cleartext_buffer[0..cleartext_len]; | |
| 1160 | 1183 | c.read_seq = std.math.add(u64, c.read_seq, 1) catch return failRead(c, error.TlsSequenceOverflow); |
| 1161 | 1184 | switch (inner_ct) { |
| 1162 | 1185 | .alert => { |
| ... | ... | @@ -1245,9 +1268,8 @@ fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize |
| 1245 | 1268 | return 0; |
| 1246 | 1269 | }, |
| 1247 | 1270 | .application_data => { |
| 1248 | if (@intFromEnum(limit) < cleartext.len) return failRead(c, error.OutputBufferUndersize); | |
| 1249 | try w.writeAll(cleartext); | |
| 1250 | return cleartext.len; | |
| 1271 | r.end += cleartext.len; | |
| 1272 | return 0; | |
| 1251 | 1273 | }, |
| 1252 | 1274 | else => return failRead(c, error.TlsUnexpectedMessage), |
| 1253 | 1275 | } |
lib/std/http.zig+35-23| ... | ... | @@ -292,6 +292,14 @@ pub const ContentEncoding = enum { |
| 292 | 292 | }); |
| 293 | 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 | 305 | pub const Connection = enum { |
| ... | ... | @@ -412,7 +420,7 @@ pub const Reader = struct { |
| 412 | 420 | /// * `interfaceDecompressing` |
| 413 | 421 | pub fn bodyReader( |
| 414 | 422 | reader: *Reader, |
| 415 | buffer: []u8, | |
| 423 | transfer_buffer: []u8, | |
| 416 | 424 | transfer_encoding: TransferEncoding, |
| 417 | 425 | content_length: ?u64, |
| 418 | 426 | ) *std.Io.Reader { |
| ... | ... | @@ -421,7 +429,7 @@ pub const Reader = struct { |
| 421 | 429 | .chunked => { |
| 422 | 430 | reader.state = .{ .body_remaining_chunk_len = .head }; |
| 423 | 431 | reader.interface = .{ |
| 424 | .buffer = buffer, | |
| 432 | .buffer = transfer_buffer, | |
| 425 | 433 | .seek = 0, |
| 426 | 434 | .end = 0, |
| 427 | 435 | .vtable = &.{ |
| ... | ... | @@ -435,7 +443,7 @@ pub const Reader = struct { |
| 435 | 443 | if (content_length) |len| { |
| 436 | 444 | reader.state = .{ .body_remaining_content_length = len }; |
| 437 | 445 | reader.interface = .{ |
| 438 | .buffer = buffer, | |
| 446 | .buffer = transfer_buffer, | |
| 439 | 447 | .seek = 0, |
| 440 | 448 | .end = 0, |
| 441 | 449 | .vtable = &.{ |
| ... | ... | @@ -460,11 +468,12 @@ pub const Reader = struct { |
| 460 | 468 | /// * `interface` |
| 461 | 469 | pub fn bodyReaderDecompressing( |
| 462 | 470 | reader: *Reader, |
| 471 | transfer_buffer: []u8, | |
| 463 | 472 | transfer_encoding: TransferEncoding, |
| 464 | 473 | content_length: ?u64, |
| 465 | 474 | content_encoding: ContentEncoding, |
| 466 | decompressor: *Decompressor, | |
| 467 | decompression_buffer: []u8, | |
| 475 | decompress: *Decompress, | |
| 476 | decompress_buffer: []u8, | |
| 468 | 477 | ) *std.Io.Reader { |
| 469 | 478 | if (transfer_encoding == .none and content_length == null) { |
| 470 | 479 | assert(reader.state == .received_head); |
| ... | ... | @@ -474,22 +483,22 @@ pub const Reader = struct { |
| 474 | 483 | return reader.in; |
| 475 | 484 | }, |
| 476 | 485 | .deflate => { |
| 477 | decompressor.* = .{ .flate = .init(reader.in, .zlib, decompression_buffer) }; | |
| 478 | return &decompressor.flate.reader; | |
| 486 | decompress.* = .{ .flate = .init(reader.in, .zlib, decompress_buffer) }; | |
| 487 | return &decompress.flate.reader; | |
| 479 | 488 | }, |
| 480 | 489 | .gzip => { |
| 481 | decompressor.* = .{ .flate = .init(reader.in, .gzip, decompression_buffer) }; | |
| 482 | return &decompressor.flate.reader; | |
| 490 | decompress.* = .{ .flate = .init(reader.in, .gzip, decompress_buffer) }; | |
| 491 | return &decompress.flate.reader; | |
| 483 | 492 | }, |
| 484 | 493 | .zstd => { |
| 485 | decompressor.* = .{ .zstd = .init(reader.in, decompression_buffer, .{ .verify_checksum = false }) }; | |
| 486 | return &decompressor.zstd.reader; | |
| 494 | decompress.* = .{ .zstd = .init(reader.in, decompress_buffer, .{ .verify_checksum = false }) }; | |
| 495 | return &decompress.zstd.reader; | |
| 487 | 496 | }, |
| 488 | 497 | .compress => unreachable, |
| 489 | 498 | } |
| 490 | 499 | } |
| 491 | const transfer_reader = bodyReader(reader, &.{}, transfer_encoding, content_length); | |
| 492 | return decompressor.init(transfer_reader, decompression_buffer, content_encoding); | |
| 500 | const transfer_reader = bodyReader(reader, transfer_buffer, transfer_encoding, content_length); | |
| 501 | return decompress.init(transfer_reader, decompress_buffer, content_encoding); | |
| 493 | 502 | } |
| 494 | 503 | |
| 495 | 504 | fn contentLengthStream( |
| ... | ... | @@ -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 | 704 | flate: std.compress.flate.Decompress, |
| 696 | 705 | zstd: std.compress.zstd.Decompress, |
| 697 | 706 | none: *std.Io.Reader, |
| 698 | 707 | |
| 699 | 708 | pub fn init( |
| 700 | decompressor: *Decompressor, | |
| 709 | decompress: *Decompress, | |
| 701 | 710 | transfer_reader: *std.Io.Reader, |
| 702 | 711 | buffer: []u8, |
| 703 | 712 | content_encoding: ContentEncoding, |
| 704 | 713 | ) *std.Io.Reader { |
| 705 | 714 | switch (content_encoding) { |
| 706 | 715 | .identity => { |
| 707 | decompressor.* = .{ .none = transfer_reader }; | |
| 716 | decompress.* = .{ .none = transfer_reader }; | |
| 708 | 717 | return transfer_reader; |
| 709 | 718 | }, |
| 710 | 719 | .deflate => { |
| 711 | decompressor.* = .{ .flate = .init(transfer_reader, .zlib, buffer) }; | |
| 712 | return &decompressor.flate.reader; | |
| 720 | decompress.* = .{ .flate = .init(transfer_reader, .zlib, buffer) }; | |
| 721 | return &decompress.flate.reader; | |
| 713 | 722 | }, |
| 714 | 723 | .gzip => { |
| 715 | decompressor.* = .{ .flate = .init(transfer_reader, .gzip, buffer) }; | |
| 716 | return &decompressor.flate.reader; | |
| 724 | decompress.* = .{ .flate = .init(transfer_reader, .gzip, buffer) }; | |
| 725 | return &decompress.flate.reader; | |
| 717 | 726 | }, |
| 718 | 727 | .zstd => { |
| 719 | decompressor.* = .{ .zstd = .init(transfer_reader, buffer, .{ .verify_checksum = false }) }; | |
| 720 | return &decompressor.zstd.reader; | |
| 728 | decompress.* = .{ .zstd = .init(transfer_reader, buffer, .{ .verify_checksum = false }) }; | |
| 729 | return &decompress.zstd.reader; | |
| 721 | 730 | }, |
| 722 | 731 | .compress => unreachable, |
| 723 | 732 | } |
| ... | ... | @@ -794,7 +803,7 @@ pub const BodyWriter = struct { |
| 794 | 803 | } |
| 795 | 804 | |
| 796 | 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 | 808 | /// When using transfer-encoding: chunked, writes the end-of-stream message |
| 800 | 809 | /// with empty trailers, then flushes the stream to the system. Asserts any |
| ... | ... | @@ -818,10 +827,13 @@ pub const BodyWriter = struct { |
| 818 | 827 | /// |
| 819 | 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 | 832 | /// See also: |
| 822 | 833 | /// * `end` |
| 823 | 834 | /// * `endChunked` |
| 824 | 835 | pub fn endUnflushed(w: *BodyWriter) Error!void { |
| 836 | try w.writer.flush(); | |
| 825 | 837 | switch (w.state) { |
| 826 | 838 | .end => unreachable, |
| 827 | 839 | .content_length => |len| { |
lib/std/http/Client.zig+22-27| ... | ... | @@ -13,8 +13,8 @@ const net = std.net; |
| 13 | 13 | const Uri = std.Uri; |
| 14 | 14 | const Allocator = mem.Allocator; |
| 15 | 15 | const assert = std.debug.assert; |
| 16 | const Writer = std.io.Writer; | |
| 17 | const Reader = std.io.Reader; | |
| 16 | const Writer = std.Io.Writer; | |
| 17 | const Reader = std.Io.Reader; | |
| 18 | 18 | |
| 19 | 19 | const Client = @This(); |
| 20 | 20 | |
| ... | ... | @@ -704,12 +704,12 @@ pub const Response = struct { |
| 704 | 704 | /// |
| 705 | 705 | /// See also: |
| 706 | 706 | /// * `readerDecompressing` |
| 707 | pub fn reader(response: *Response, buffer: []u8) *Reader { | |
| 707 | pub fn reader(response: *Response, transfer_buffer: []u8) *Reader { | |
| 708 | 708 | response.head.invalidateStrings(); |
| 709 | 709 | const req = response.request; |
| 710 | 710 | if (!req.method.responseHasBody()) return .ending; |
| 711 | 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 | 715 | /// If compressed body has been negotiated this will return decompressed bytes. |
| ... | ... | @@ -723,17 +723,19 @@ pub const Response = struct { |
| 723 | 723 | /// * `reader` |
| 724 | 724 | pub fn readerDecompressing( |
| 725 | 725 | response: *Response, |
| 726 | decompressor: *http.Decompressor, | |
| 727 | decompression_buffer: []u8, | |
| 726 | transfer_buffer: []u8, | |
| 727 | decompress: *http.Decompress, | |
| 728 | decompress_buffer: []u8, | |
| 728 | 729 | ) *Reader { |
| 729 | 730 | response.head.invalidateStrings(); |
| 730 | 731 | const head = &response.head; |
| 731 | 732 | return response.request.reader.bodyReaderDecompressing( |
| 733 | transfer_buffer, | |
| 732 | 734 | head.transfer_encoding, |
| 733 | 735 | head.content_length, |
| 734 | 736 | head.content_encoding, |
| 735 | decompressor, | |
| 736 | decompression_buffer, | |
| 737 | decompress, | |
| 738 | decompress_buffer, | |
| 737 | 739 | ); |
| 738 | 740 | } |
| 739 | 741 | |
| ... | ... | @@ -1322,7 +1324,7 @@ pub const basic_authorization = struct { |
| 1322 | 1324 | const user: Uri.Component = uri.user orelse .empty; |
| 1323 | 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 | 1328 | user.formatUser(&dw.writer) catch unreachable; // discarding |
| 1327 | 1329 | const user_len = dw.count + dw.writer.end; |
| 1328 | 1330 | |
| ... | ... | @@ -1696,8 +1698,8 @@ pub const FetchOptions = struct { |
| 1696 | 1698 | /// `null` means it will be heap-allocated. |
| 1697 | 1699 | decompress_buffer: ?[]u8 = null, |
| 1698 | 1700 | redirect_behavior: ?Request.RedirectBehavior = null, |
| 1699 | /// If the server sends a body, it will be stored here. | |
| 1700 | response_storage: ?ResponseStorage = null, | |
| 1701 | /// If the server sends a body, it will be written here. | |
| 1702 | response_writer: ?*Writer = null, | |
| 1701 | 1703 | |
| 1702 | 1704 | location: Location, |
| 1703 | 1705 | method: ?http.Method = null, |
| ... | ... | @@ -1725,7 +1727,7 @@ pub const FetchOptions = struct { |
| 1725 | 1727 | list: *std.ArrayListUnmanaged(u8), |
| 1726 | 1728 | /// If null then only the existing capacity will be used. |
| 1727 | 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 | 1780 | |
| 1779 | 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 | 1784 | const reader = response.reader(&.{}); |
| 1783 | 1785 | _ = reader.discardRemaining() catch |err| switch (err) { |
| 1784 | 1786 | error.ReadFailed => return response.bodyErr().?, |
| ... | ... | @@ -1794,21 +1796,14 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult { |
| 1794 | 1796 | }; |
| 1795 | 1797 | defer if (options.decompress_buffer == null) client.allocator.free(decompress_buffer); |
| 1796 | 1798 | |
| 1797 | var decompressor: http.Decompressor = undefined; | |
| 1798 | const reader = response.readerDecompressing(&decompressor, decompress_buffer); | |
| 1799 | const list = storage.list; | |
| 1799 | var transfer_buffer: [64]u8 = undefined; | |
| 1800 | var decompress: http.Decompress = undefined; | |
| 1801 | const reader = response.readerDecompressing(&transfer_buffer, &decompress, decompress_buffer); | |
| 1800 | 1802 | |
| 1801 | if (storage.allocator) |allocator| { | |
| 1802 | reader.appendRemaining(allocator, null, list, storage.append_limit) catch |err| switch (err) { | |
| 1803 | error.ReadFailed => return response.bodyErr().?, | |
| 1804 | else => |e| return e, | |
| 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 | } | |
| 1803 | _ = reader.streamRemaining(response_writer) catch |err| switch (err) { | |
| 1804 | error.ReadFailed => return response.bodyErr().?, | |
| 1805 | else => |e| return e, | |
| 1806 | }; | |
| 1812 | 1807 | |
| 1813 | 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 | 1006 | const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}/echo-content#fetch", .{port}); |
| 1007 | 1007 | defer gpa.free(location); |
| 1008 | 1008 | |
| 1009 | var body: std.ArrayListUnmanaged(u8) = .empty; | |
| 1010 | defer body.deinit(gpa); | |
| 1009 | var body: std.Io.Writer.Allocating = .init(gpa); | |
| 1010 | defer body.deinit(); | |
| 1011 | try body.ensureUnusedCapacity(64); | |
| 1011 | 1012 | |
| 1012 | 1013 | const res = try client.fetch(.{ |
| 1013 | 1014 | .location = .{ .url = location }, |
| ... | ... | @@ -1016,10 +1017,10 @@ fn echoTests(client: *http.Client, port: u16) !void { |
| 1016 | 1017 | .extra_headers = &.{ |
| 1017 | 1018 | .{ .name = "content-type", .value = "text/plain" }, |
| 1018 | 1019 | }, |
| 1019 | .response_storage = .{ .allocator = gpa, .list = &body }, | |
| 1020 | .response_writer = &body.writer, | |
| 1020 | 1021 | }); |
| 1021 | 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 | 1026 | { // expect: 100-continue |
src/Package/Fetch.zig+24-18| ... | ... | @@ -883,7 +883,9 @@ const Resource = union(enum) { |
| 883 | 883 | const HttpRequest = struct { |
| 884 | 884 | request: std.http.Client.Request, |
| 885 | 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 | 891 | fn deinit(resource: *Resource) void { |
| ... | ... | @@ -892,7 +894,6 @@ const Resource = union(enum) { |
| 892 | 894 | .http_request => |*http_request| http_request.request.deinit(), |
| 893 | 895 | .git => |*git_resource| { |
| 894 | 896 | git_resource.fetch_stream.deinit(); |
| 895 | git_resource.session.deinit(); | |
| 896 | 897 | }, |
| 897 | 898 | .dir => |*dir| dir.close(), |
| 898 | 899 | } |
| ... | ... | @@ -902,7 +903,11 @@ const Resource = union(enum) { |
| 902 | 903 | fn reader(resource: *Resource) *std.Io.Reader { |
| 903 | 904 | return switch (resource.*) { |
| 904 | 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 | 911 | .git => |*g| return &g.fetch_stream.reader, |
| 907 | 912 | .dir => unreachable, |
| 908 | 913 | }; |
| ... | ... | @@ -971,7 +976,6 @@ const FileType = enum { |
| 971 | 976 | const init_resource_buffer_size = git.Packet.max_data_length; |
| 972 | 977 | |
| 973 | 978 | fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u8) RunError!void { |
| 974 | const gpa = f.arena.child_allocator; | |
| 975 | 979 | const arena = f.arena.allocator(); |
| 976 | 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 | 999 | .request = http_client.request(.GET, uri, .{}) catch |err| |
| 996 | 1000 | return f.fail(f.location_tok, try eb.printString("unable to connect to server: {t}", .{err})), |
| 997 | 1001 | .response = undefined, |
| 998 | .buffer = reader_buffer, | |
| 1002 | .transfer_buffer = reader_buffer, | |
| 1003 | .decompress_buffer = &.{}, | |
| 1004 | .decompress = undefined, | |
| 999 | 1005 | } }; |
| 1000 | 1006 | const request = &resource.http_request.request; |
| 1001 | 1007 | errdefer request.deinit(); |
| ... | ... | @@ -1019,6 +1025,7 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u |
| 1019 | 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 | 1029 | return; |
| 1023 | 1030 | } |
| 1024 | 1031 | |
| ... | ... | @@ -1027,13 +1034,12 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u |
| 1027 | 1034 | { |
| 1028 | 1035 | var transport_uri = uri; |
| 1029 | 1036 | transport_uri.scheme = uri.scheme["git+".len..]; |
| 1030 | var session = git.Session.init(gpa, http_client, transport_uri, reader_buffer) catch |err| { | |
| 1031 | return f.fail(f.location_tok, try eb.printString( | |
| 1032 | "unable to discover remote git server capabilities: {s}", | |
| 1033 | .{@errorName(err)}, | |
| 1034 | )); | |
| 1037 | var session = git.Session.init(arena, http_client, transport_uri, reader_buffer) catch |err| { | |
| 1038 | return f.fail( | |
| 1039 | f.location_tok, | |
| 1040 | try eb.printString("unable to discover remote git server capabilities: {t}", .{err}), | |
| 1041 | ); | |
| 1035 | 1042 | }; |
| 1036 | errdefer session.deinit(); | |
| 1037 | 1043 | |
| 1038 | 1044 | const want_oid = want_oid: { |
| 1039 | 1045 | const want_ref = |
| ... | ... | @@ -1086,17 +1092,17 @@ fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u |
| 1086 | 1092 | |
| 1087 | 1093 | var want_oid_buf: [git.Oid.max_formatted_length]u8 = undefined; |
| 1088 | 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 | 1095 | resource.* = .{ .git = .{ |
| 1096 | 1096 | .session = session, |
| 1097 | .fetch_stream = fetch_stream, | |
| 1097 | .fetch_stream = undefined, | |
| 1098 | 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 | 1106 | return; |
| 1101 | 1107 | } |
| 1102 | 1108 |
src/Package/Fetch/git.zig+40-57| ... | ... | @@ -644,7 +644,7 @@ pub const Session = struct { |
| 644 | 644 | supports_agent: bool, |
| 645 | 645 | supports_shallow: bool, |
| 646 | 646 | object_format: Oid.Format, |
| 647 | allocator: Allocator, | |
| 647 | arena: Allocator, | |
| 648 | 648 | |
| 649 | 649 | const agent = "zig/" ++ @import("builtin").zig_version_string; |
| 650 | 650 | const agent_capability = std.fmt.comptimePrint("agent={s}\n", .{agent}); |
| ... | ... | @@ -652,7 +652,7 @@ pub const Session = struct { |
| 652 | 652 | /// Initializes a client session and discovers the capabilities of the |
| 653 | 653 | /// server for optimal transport. |
| 654 | 654 | pub fn init( |
| 655 | allocator: Allocator, | |
| 655 | arena: Allocator, | |
| 656 | 656 | transport: *std.http.Client, |
| 657 | 657 | uri: std.Uri, |
| 658 | 658 | /// Asserted to be at least `Packet.max_data_length` |
| ... | ... | @@ -661,13 +661,12 @@ pub const Session = struct { |
| 661 | 661 | assert(response_buffer.len >= Packet.max_data_length); |
| 662 | 662 | var session: Session = .{ |
| 663 | 663 | .transport = transport, |
| 664 | .location = try .init(allocator, uri), | |
| 664 | .location = try .init(arena, uri), | |
| 665 | 665 | .supports_agent = false, |
| 666 | 666 | .supports_shallow = false, |
| 667 | 667 | .object_format = .sha1, |
| 668 | .allocator = allocator, | |
| 668 | .arena = arena, | |
| 669 | 669 | }; |
| 670 | errdefer session.deinit(); | |
| 671 | 670 | var capability_iterator: CapabilityIterator = undefined; |
| 672 | 671 | try session.getCapabilities(&capability_iterator, response_buffer); |
| 673 | 672 | defer capability_iterator.deinit(); |
| ... | ... | @@ -690,34 +689,24 @@ pub const Session = struct { |
| 690 | 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 | 692 | /// An owned `std.Uri` representing the location of the server (base URI). |
| 699 | 693 | const Location = struct { |
| 700 | 694 | uri: std.Uri, |
| 701 | 695 | |
| 702 | fn init(allocator: Allocator, uri: std.Uri) !Location { | |
| 703 | const scheme = try allocator.dupe(u8, uri.scheme); | |
| 704 | errdefer allocator.free(scheme); | |
| 705 | const user = if (uri.user) |user| try std.fmt.allocPrint(allocator, "{f}", .{ | |
| 696 | fn init(arena: Allocator, uri: std.Uri) !Location { | |
| 697 | const scheme = try arena.dupe(u8, uri.scheme); | |
| 698 | const user = if (uri.user) |user| try std.fmt.allocPrint(arena, "{f}", .{ | |
| 706 | 699 | std.fmt.alt(user, .formatUser), |
| 707 | 700 | }) else null; |
| 708 | errdefer if (user) |s| allocator.free(s); | |
| 709 | const password = if (uri.password) |password| try std.fmt.allocPrint(allocator, "{f}", .{ | |
| 701 | const password = if (uri.password) |password| try std.fmt.allocPrint(arena, "{f}", .{ | |
| 710 | 702 | std.fmt.alt(password, .formatPassword), |
| 711 | 703 | }) else null; |
| 712 | errdefer if (password) |s| allocator.free(s); | |
| 713 | const host = if (uri.host) |host| try std.fmt.allocPrint(allocator, "{f}", .{ | |
| 704 | const host = if (uri.host) |host| try std.fmt.allocPrint(arena, "{f}", .{ | |
| 714 | 705 | std.fmt.alt(host, .formatHost), |
| 715 | 706 | }) else null; |
| 716 | errdefer if (host) |s| allocator.free(s); | |
| 717 | const path = try std.fmt.allocPrint(allocator, "{f}", .{ | |
| 707 | const path = try std.fmt.allocPrint(arena, "{f}", .{ | |
| 718 | 708 | std.fmt.alt(uri.path, .formatPath), |
| 719 | 709 | }); |
| 720 | errdefer allocator.free(path); | |
| 721 | 710 | // The query and fragment are not used as part of the base server URI. |
| 722 | 711 | return .{ |
| 723 | 712 | .uri = .{ |
| ... | ... | @@ -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 | 724 | /// Returns an iterator over capabilities supported by the server. |
| ... | ... | @@ -745,16 +726,17 @@ pub const Session = struct { |
| 745 | 726 | /// The `session.location` is updated if the server returns a redirect, so |
| 746 | 727 | /// that subsequent session functions do not need to handle redirects. |
| 747 | 728 | fn getCapabilities(session: *Session, it: *CapabilityIterator, response_buffer: []u8) !void { |
| 729 | const arena = session.arena; | |
| 748 | 730 | assert(response_buffer.len >= Packet.max_data_length); |
| 749 | 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 | 734 | std.fmt.alt(session.location.uri.path, .formatPath), |
| 753 | 735 | }); |
| 754 | defer session.allocator.free(session_uri_path); | |
| 755 | info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "info/refs" }) }; | |
| 736 | info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(arena, &.{ | |
| 737 | "/", session_uri_path, "info/refs", | |
| 738 | }) }; | |
| 756 | 739 | } |
| 757 | defer session.allocator.free(info_refs_uri.path.percent_encoded); | |
| 758 | 740 | info_refs_uri.query = .{ .percent_encoded = "service=git-upload-pack" }; |
| 759 | 741 | info_refs_uri.fragment = null; |
| 760 | 742 | |
| ... | ... | @@ -767,6 +749,7 @@ pub const Session = struct { |
| 767 | 749 | }, |
| 768 | 750 | }), |
| 769 | 751 | .reader = undefined, |
| 752 | .decompress = undefined, | |
| 770 | 753 | }; |
| 771 | 754 | errdefer it.deinit(); |
| 772 | 755 | const request = &it.request; |
| ... | ... | @@ -777,19 +760,17 @@ pub const Session = struct { |
| 777 | 760 | if (response.head.status != .ok) return error.ProtocolError; |
| 778 | 761 | const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects; |
| 779 | 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 | 764 | std.fmt.alt(request.uri.path, .formatPath), |
| 782 | 765 | }); |
| 783 | defer session.allocator.free(request_uri_path); | |
| 784 | 766 | if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect; |
| 785 | 767 | var new_uri = request.uri; |
| 786 | 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); | |
| 788 | session.location.deinit(session.allocator); | |
| 789 | session.location = new_location; | |
| 769 | session.location = try .init(arena, new_uri); | |
| 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 | 774 | var state: enum { response_start, response_content } = .response_start; |
| 794 | 775 | while (true) { |
| 795 | 776 | // Some Git servers (at least GitHub) include an additional |
| ... | ... | @@ -821,6 +802,7 @@ pub const Session = struct { |
| 821 | 802 | const CapabilityIterator = struct { |
| 822 | 803 | request: std.http.Client.Request, |
| 823 | 804 | reader: *std.Io.Reader, |
| 805 | decompress: std.http.Decompress, | |
| 824 | 806 | |
| 825 | 807 | const Capability = struct { |
| 826 | 808 | key: []const u8, |
| ... | ... | @@ -864,16 +846,15 @@ pub const Session = struct { |
| 864 | 846 | |
| 865 | 847 | /// Returns an iterator over refs known to the server. |
| 866 | 848 | pub fn listRefs(session: Session, it: *RefIterator, options: ListRefsOptions) !void { |
| 849 | const arena = session.arena; | |
| 867 | 850 | assert(options.buffer.len >= Packet.max_data_length); |
| 868 | 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 | 854 | std.fmt.alt(session.location.uri.path, .formatPath), |
| 872 | 855 | }); |
| 873 | defer session.allocator.free(session_uri_path); | |
| 874 | upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; | |
| 856 | upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(arena, &.{ "/", session_uri_path, "git-upload-pack" }) }; | |
| 875 | 857 | } |
| 876 | defer session.allocator.free(upload_pack_uri.path.percent_encoded); | |
| 877 | 858 | upload_pack_uri.query = null; |
| 878 | 859 | upload_pack_uri.fragment = null; |
| 879 | 860 | |
| ... | ... | @@ -883,16 +864,14 @@ pub const Session = struct { |
| 883 | 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 | 868 | session.object_format, |
| 888 | 869 | }); |
| 889 | defer session.allocator.free(object_format_packet); | |
| 890 | 870 | try Packet.write(.{ .data = object_format_packet }, &body); |
| 891 | 871 | } |
| 892 | 872 | try Packet.write(.delimiter, &body); |
| 893 | 873 | for (options.ref_prefixes) |ref_prefix| { |
| 894 | const ref_prefix_packet = try std.fmt.allocPrint(session.allocator, "ref-prefix {s}\n", .{ref_prefix}); | |
| 895 | defer session.allocator.free(ref_prefix_packet); | |
| 874 | const ref_prefix_packet = try std.fmt.allocPrint(arena, "ref-prefix {s}\n", .{ref_prefix}); | |
| 896 | 875 | try Packet.write(.{ .data = ref_prefix_packet }, &body); |
| 897 | 876 | } |
| 898 | 877 | if (options.include_symrefs) { |
| ... | ... | @@ -913,6 +892,7 @@ pub const Session = struct { |
| 913 | 892 | }), |
| 914 | 893 | .reader = undefined, |
| 915 | 894 | .format = session.object_format, |
| 895 | .decompress = undefined, | |
| 916 | 896 | }; |
| 917 | 897 | const request = &it.request; |
| 918 | 898 | errdefer request.deinit(); |
| ... | ... | @@ -920,13 +900,15 @@ pub const Session = struct { |
| 920 | 900 | |
| 921 | 901 | var response = try request.receiveHead(options.buffer); |
| 922 | 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 | 907 | pub const RefIterator = struct { |
| 927 | 908 | format: Oid.Format, |
| 928 | 909 | request: std.http.Client.Request, |
| 929 | 910 | reader: *std.Io.Reader, |
| 911 | decompress: std.http.Decompress, | |
| 930 | 912 | |
| 931 | 913 | pub const Ref = struct { |
| 932 | 914 | oid: Oid, |
| ... | ... | @@ -981,16 +963,15 @@ pub const Session = struct { |
| 981 | 963 | /// Asserted to be at least `Packet.max_data_length`. |
| 982 | 964 | response_buffer: []u8, |
| 983 | 965 | ) !void { |
| 966 | const arena = session.arena; | |
| 984 | 967 | assert(response_buffer.len >= Packet.max_data_length); |
| 985 | 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 | 971 | std.fmt.alt(session.location.uri.path, .formatPath), |
| 989 | 972 | }); |
| 990 | defer session.allocator.free(session_uri_path); | |
| 991 | upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; | |
| 973 | upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(arena, &.{ "/", session_uri_path, "git-upload-pack" }) }; | |
| 992 | 974 | } |
| 993 | defer session.allocator.free(upload_pack_uri.path.percent_encoded); | |
| 994 | 975 | upload_pack_uri.query = null; |
| 995 | 976 | upload_pack_uri.fragment = null; |
| 996 | 977 | |
| ... | ... | @@ -1000,8 +981,7 @@ pub const Session = struct { |
| 1000 | 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)}); | |
| 1004 | defer session.allocator.free(object_format_packet); | |
| 984 | const object_format_packet = try std.fmt.allocPrint(arena, "object-format={s}\n", .{@tagName(session.object_format)}); | |
| 1005 | 985 | try Packet.write(.{ .data = object_format_packet }, &body); |
| 1006 | 986 | } |
| 1007 | 987 | try Packet.write(.delimiter, &body); |
| ... | ... | @@ -1031,6 +1011,7 @@ pub const Session = struct { |
| 1031 | 1011 | .input = undefined, |
| 1032 | 1012 | .reader = undefined, |
| 1033 | 1013 | .remaining_len = undefined, |
| 1014 | .decompress = undefined, | |
| 1034 | 1015 | }; |
| 1035 | 1016 | const request = &fs.request; |
| 1036 | 1017 | errdefer request.deinit(); |
| ... | ... | @@ -1040,7 +1021,8 @@ pub const Session = struct { |
| 1040 | 1021 | var response = try request.receiveHead(&.{}); |
| 1041 | 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 | 1026 | // We are not interested in any of the sections of the returned fetch |
| 1045 | 1027 | // data other than the packfile section, since we aren't doing anything |
| 1046 | 1028 | // complex like ref negotiation (this is a fresh clone). |
| ... | ... | @@ -1079,6 +1061,7 @@ pub const Session = struct { |
| 1079 | 1061 | reader: std.Io.Reader, |
| 1080 | 1062 | err: ?Error = null, |
| 1081 | 1063 | remaining_len: usize, |
| 1064 | decompress: std.http.Decompress, | |
| 1082 | 1065 | |
| 1083 | 1066 | pub fn deinit(fs: *FetchStream) void { |
| 1084 | 1067 | fs.request.deinit(); |
| ... | ... | @@ -1131,8 +1114,8 @@ pub const Session = struct { |
| 1131 | 1114 | } |
| 1132 | 1115 | const buf = limit.slice(try w.writableSliceGreedy(1)); |
| 1133 | 1116 | const n = @min(buf.len, fs.remaining_len); |
| 1134 | @memcpy(buf[0..n], input.buffered()[0..n]); | |
| 1135 | input.toss(n); | |
| 1117 | try input.readSliceAll(buf[0..n]); | |
| 1118 | w.advance(n); | |
| 1136 | 1119 | fs.remaining_len -= n; |
| 1137 | 1120 | return n; |
| 1138 | 1121 | } |