authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-26 13:09:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:30-07:00
log9f8486170c5a5dda56381098cf10ecf8c6364d9b
tree0e53d8f0eac7314e56733eb24162debf9a89547c
parentd4545f216a6ea86caec208cabc724004b7ef320a

std.http compiling again


13 files changed, 290 insertions(+), 237 deletions(-)

lib/std/Build.zig+1-1
......@@ -2061,7 +2061,7 @@ pub fn runAllowFail(
20612061 try child.spawn();
20622062
20632063 var file_reader = child.stdout.?.readerStreaming();
2064 const stdout = try file_reader.interface().readRemainingAlloc(b.allocator, .limited(max_output_size));
2064 const stdout = try file_reader.interface().allocRemaining(b.allocator, .limited(max_output_size));
20652065 errdefer b.allocator.free(stdout);
20662066
20672067 const term = try child.wait();
lib/std/Build/Cache.zig+1-1
......@@ -663,7 +663,7 @@ pub const Manifest = struct {
663663 const input_file_count = self.files.entries.len;
664664 var manifest_reader = self.manifest_file.?.reader(); // Reads positionally from zero.
665665 const limit: std.io.Limit = .limited(manifest_file_size_max);
666 const file_contents = manifest_reader.interface().readRemainingAlloc(gpa, limit) catch |err| switch (err) {
666 const file_contents = manifest_reader.interface().allocRemaining(gpa, limit) catch |err| switch (err) {
667667 error.OutOfMemory => return error.OutOfMemory,
668668 error.StreamTooLong => return error.OutOfMemory,
669669 error.ReadFailed => {
lib/std/Build/Step/Run.zig+2-2
......@@ -1788,7 +1788,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult {
17881788 stderr_bytes = poller.reader(.stderr).buffered();
17891789 } else {
17901790 var fr = stdout.readerStreaming();
1791 stdout_bytes = fr.interface().readRemainingAlloc(arena, run.stdio_limit) catch |err| switch (err) {
1791 stdout_bytes = fr.interface().allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
17921792 error.OutOfMemory => return error.OutOfMemory,
17931793 error.ReadFailed => return fr.err.?,
17941794 error.StreamTooLong => return error.StdoutStreamTooLong,
......@@ -1796,7 +1796,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult {
17961796 }
17971797 } else if (child.stderr) |stderr| {
17981798 var fr = stderr.readerStreaming();
1799 stderr_bytes = fr.interface().readRemainingAlloc(arena, run.stdio_limit) catch |err| switch (err) {
1799 stderr_bytes = fr.interface().allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
18001800 error.OutOfMemory => return error.OutOfMemory,
18011801 error.ReadFailed => return fr.err.?,
18021802 error.StreamTooLong => return error.StderrStreamTooLong,
lib/std/Uri.zig+2-2
......@@ -465,11 +465,11 @@ fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Co
465465 var aux: Writer = .fixed(aux_buf.*);
466466 if (!base.isEmpty()) {
467467 aux.print("{fpath}", .{base}) catch return error.NoSpaceLeft;
468 aux.end = std.mem.lastIndexOfScalar(u8, aux.getWritten(), '/') orelse
468 aux.end = std.mem.lastIndexOfScalar(u8, aux.buffered(), '/') orelse
469469 return remove_dot_segments(new);
470470 }
471471 aux.print("/{s}", .{new}) catch return error.NoSpaceLeft;
472 const merged_path = remove_dot_segments(aux.getWritten());
472 const merged_path = remove_dot_segments(aux.buffered());
473473 aux_buf.* = aux_buf.*[merged_path.percent_encoded.len..];
474474 return merged_path;
475475}
lib/std/compress/flate/Decompress.zig+6-6
......@@ -8,7 +8,7 @@ const Writer = std.io.Writer;
88const Reader = std.io.Reader;
99
1010input: *Reader,
11interface: Reader,
11reader: Reader,
1212/// Hashes, produces checksum, of uncompressed data for gzip/zlib footer.
1313hasher: Container.Hasher,
1414
......@@ -51,7 +51,7 @@ pub const Error = Container.Error || error{
5151
5252pub fn init(input: *Reader, container: Container, buffer: []u8) Decompress {
5353 return .{
54 .interface = .{
54 .reader = .{
5555 // TODO populate discard so that when an amount is discarded that
5656 // includes an entire frame, skip decoding that frame.
5757 .vtable = &.{ .stream = stream },
......@@ -130,7 +130,7 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
130130}
131131
132132pub fn stream(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
133 const d: *Decompress = @alignCast(@fieldParentPtr("interface", r));
133 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
134134 return readInner(d, w, limit) catch |err| switch (err) {
135135 error.EndOfStream => return error.EndOfStream,
136136 error.WriteFailed => return error.WriteFailed,
......@@ -247,7 +247,7 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.io.Limit) (Error || Reader.S
247247 }
248248 },
249249 .stored_block => |remaining_len| {
250 const out = try w.writableSliceGreedyPreserving(flate.history_len, 1);
250 const out = try w.writableSliceGreedyPreserve(flate.history_len, 1);
251251 const limited_out = limit.min(.limited(remaining_len)).slice(out);
252252 const n = try d.input.readVec(&.{limited_out});
253253 if (remaining_len - n == 0) {
......@@ -263,7 +263,7 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.io.Limit) (Error || Reader.S
263263 while (@intFromEnum(limit) > w.count - start) {
264264 const code = try d.readFixedCode();
265265 switch (code) {
266 0...255 => try w.writeBytePreserving(flate.history_len, @intCast(code)),
266 0...255 => try w.writeBytePreserve(flate.history_len, @intCast(code)),
267267 256 => {
268268 d.state = if (d.final_block) .protocol_footer else .block_header;
269269 return w.count - start;
......@@ -289,7 +289,7 @@ fn readInner(d: *Decompress, w: *Writer, limit: std.io.Limit) (Error || Reader.S
289289 const sym = try d.decodeSymbol(&d.lit_dec);
290290
291291 switch (sym.kind) {
292 .literal => try w.writeBytePreserving(flate.history_len, sym.symbol),
292 .literal => try w.writeBytePreserve(flate.history_len, sym.symbol),
293293 .match => {
294294 // Decode match backreference <length, distance>
295295 const length = try d.decodeLength(sym.symbol);
lib/std/compress/zstd/Decompress.zig+3-3
......@@ -7,7 +7,7 @@ const zstd = @import("../zstd.zig");
77const Writer = std.io.Writer;
88
99input: *Reader,
10interface: Reader,
10reader: Reader,
1111state: State,
1212verify_checksum: bool,
1313err: ?Error = null,
......@@ -68,7 +68,7 @@ pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
6868 .input = input,
6969 .state = .new_frame,
7070 .verify_checksum = options.verify_checksum,
71 .interface = .{
71 .reader = .{
7272 .vtable = &.{ .stream = stream },
7373 .buffer = buffer,
7474 .seek = 0,
......@@ -78,7 +78,7 @@ pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
7878}
7979
8080fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
81 const d: *Decompress = @alignCast(@fieldParentPtr("interface", r));
81 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
8282 const in = d.input;
8383
8484 switch (d.state) {
lib/std/debug/Dwarf.zig+1-1
......@@ -2242,7 +2242,7 @@ pub const ElfModule = struct {
22422242
22432243 var zlib_stream: std.compress.flate.Decompress = .init(&section_reader, .zlib, &.{});
22442244
2245 const decompressed_section = zlib_stream.interface.allocRemaining(gpa, .limited(ch_size)) catch
2245 const decompressed_section = zlib_stream.reader.allocRemaining(gpa, .limited(ch_size)) catch
22462246 continue;
22472247 if (decompressed_section.len != ch_size) {
22482248 gpa.free(decompressed_section);
lib/std/http.zig+49-55
......@@ -328,6 +328,9 @@ pub const Header = struct {
328328
329329pub const Reader = struct {
330330 in: *std.io.Reader,
331 /// This is preallocated memory that might be used by `bodyReader`. That
332 /// function might return a pointer to this field, or a different
333 /// `*std.io.Reader`. Advisable to not access this field directly.
331334 interface: std.io.Reader,
332335 /// Keeps track of whether the stream is ready to accept a new request,
333336 /// making invalid API usage cause assertion failures rather than HTTP
......@@ -489,31 +492,31 @@ pub const Reader = struct {
489492 content_encoding: ContentEncoding,
490493 decompressor: *Decompressor,
491494 decompression_buffer: []u8,
492 ) std.io.Reader {
495 ) *std.io.Reader {
493496 if (transfer_encoding == .none and content_length == null) {
494497 assert(reader.state == .received_head);
495498 reader.state = .body_none;
496499 switch (content_encoding) {
497500 .identity => {
498 return reader.in.reader();
501 return reader.in;
499502 },
500503 .deflate => {
501 decompressor.compression = .{ .deflate = .init(reader.in) };
502 return decompressor.compression.deflate.reader();
504 decompressor.* = .{ .flate = .init(reader.in, .raw, decompression_buffer) };
505 return &decompressor.flate.reader;
503506 },
504507 .gzip => {
505 decompressor.compression = .{ .gzip = .init(reader.in) };
506 return decompressor.compression.gzip.reader();
508 decompressor.* = .{ .flate = .init(reader.in, .gzip, decompression_buffer) };
509 return &decompressor.flate.reader;
507510 },
508511 .zstd => {
509 decompressor.compression = .{ .zstd = .init(reader.in, .{ .verify_checksum = false }) };
510 return decompressor.compression.zstd.reader();
512 decompressor.* = .{ .zstd = .init(reader.in, decompression_buffer, .{ .verify_checksum = false }) };
513 return &decompressor.zstd.reader;
511514 },
512515 .compress => unreachable,
513516 }
514517 }
515 const transfer_reader = bodyReader(reader, transfer_encoding, content_length);
516 return decompressor.reader(transfer_reader, decompression_buffer, content_encoding);
518 const transfer_reader = bodyReader(reader, &.{}, transfer_encoding, content_length);
519 return decompressor.init(transfer_reader, decompression_buffer, content_encoding);
517520 }
518521
519522 fn contentLengthStream(
......@@ -711,42 +714,33 @@ pub const Reader = struct {
711714 }
712715};
713716
714pub const Decompressor = struct {
715 compression: Compression,
716 buffered_reader: std.io.Reader,
717
718 pub const Compression = union(enum) {
719 deflate: std.compress.flate.Decompressor,
720 gzip: std.compress.flate.Decompressor,
721 zstd: std.compress.zstd.Decompress,
722 none: void,
723 };
717pub const Decompressor = union(enum) {
718 flate: std.compress.flate.Decompress,
719 zstd: std.compress.zstd.Decompress,
720 none: *std.io.Reader,
724721
725 pub fn reader(
722 pub fn init(
726723 decompressor: *Decompressor,
727 transfer_reader: std.io.Reader,
724 transfer_reader: *std.io.Reader,
728725 buffer: []u8,
729726 content_encoding: ContentEncoding,
730 ) std.io.Reader {
727 ) *std.io.Reader {
731728 switch (content_encoding) {
732729 .identity => {
733 decompressor.compression = .none;
730 decompressor.* = .{ .none = transfer_reader };
734731 return transfer_reader;
735732 },
736733 .deflate => {
737 decompressor.buffered_reader = transfer_reader.buffered(buffer);
738 decompressor.compression = .{ .deflate = .init(&decompressor.buffered_reader) };
739 return decompressor.compression.deflate.reader();
734 decompressor.* = .{ .flate = .init(transfer_reader, .raw, buffer) };
735 return &decompressor.flate.reader;
740736 },
741737 .gzip => {
742 decompressor.buffered_reader = transfer_reader.buffered(buffer);
743 decompressor.compression = .{ .gzip = .init(&decompressor.buffered_reader) };
744 return decompressor.compression.gzip.reader();
738 decompressor.* = .{ .flate = .init(transfer_reader, .gzip, buffer) };
739 return &decompressor.flate.reader;
745740 },
746741 .zstd => {
747 decompressor.buffered_reader = transfer_reader.buffered(buffer);
748 decompressor.compression = .{ .zstd = .init(&decompressor.buffered_reader, .{}) };
749 return decompressor.compression.gzip.reader();
742 decompressor.* = .{ .zstd = .init(transfer_reader, buffer, .{ .verify_checksum = false }) };
743 return &decompressor.zstd.reader;
750744 },
751745 .compress => unreachable,
752746 }
......@@ -759,7 +753,7 @@ pub const BodyWriter = struct {
759753 /// state of this other than via methods of `BodyWriter`.
760754 http_protocol_output: *Writer,
761755 state: State,
762 interface: Writer,
756 writer: Writer,
763757
764758 pub const Error = Writer.Error;
765759
......@@ -797,7 +791,7 @@ pub const BodyWriter = struct {
797791 };
798792
799793 pub fn isEliding(w: *const BodyWriter) bool {
800 return w.interface.vtable.drain == Writer.discardingDrain;
794 return w.writer.vtable.drain == Writer.discardingDrain;
801795 }
802796
803797 /// Sends all buffered data across `BodyWriter.http_protocol_output`.
......@@ -924,42 +918,42 @@ pub const BodyWriter = struct {
924918 w.state = .end;
925919 }
926920
927 fn contentLengthDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
928 const bw: *BodyWriter = @fieldParentPtr("interface", w);
921 pub fn contentLengthDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
922 const bw: *BodyWriter = @fieldParentPtr("writer", w);
929923 assert(!bw.isEliding());
930 const out = w.http_protocol_output;
924 const out = bw.http_protocol_output;
931925 const n = try w.drainTo(out, data, splat);
932 w.state.content_length -= n;
926 bw.state.content_length -= n;
933927 return n;
934928 }
935929
936 fn noneDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
937 const bw: *BodyWriter = @fieldParentPtr("interface", w);
930 pub fn noneDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
931 const bw: *BodyWriter = @fieldParentPtr("writer", w);
938932 assert(!bw.isEliding());
939 const out = w.http_protocol_output;
933 const out = bw.http_protocol_output;
940934 return try w.drainTo(out, data, splat);
941935 }
942936
943937 /// Returns `null` if size cannot be computed without making any syscalls.
944 fn noneSendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) Writer.FileError!usize {
945 const bw: *BodyWriter = @fieldParentPtr("interface", w);
938 pub fn noneSendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) Writer.FileError!usize {
939 const bw: *BodyWriter = @fieldParentPtr("writer", w);
946940 assert(!bw.isEliding());
947941 return w.sendFileTo(bw.http_protocol_output, file_reader, limit);
948942 }
949943
950 fn contentLengthSendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) Writer.FileError!usize {
951 const bw: *BodyWriter = @fieldParentPtr("interface", w);
944 pub fn contentLengthSendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) Writer.FileError!usize {
945 const bw: *BodyWriter = @fieldParentPtr("writer", w);
952946 assert(!bw.isEliding());
953947 const n = try w.sendFileTo(bw.http_protocol_output, file_reader, limit);
954948 bw.state.content_length -= n;
955949 return n;
956950 }
957951
958 fn chunkedSendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) Writer.FileError!usize {
959 const bw: *BodyWriter = @fieldParentPtr("interface", w);
952 pub fn chunkedSendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) Writer.FileError!usize {
953 const bw: *BodyWriter = @fieldParentPtr("writer", w);
960954 assert(!bw.isEliding());
961 const data_len = w.countSendFileUpperBound(file_reader, limit) orelse {
962 // If the file size is unknown, we cannot lower to a `writeFile` since we would
955 const data_len = if (file_reader.getSize()) |x| w.end + x else |_| {
956 // If the file size is unknown, we cannot lower to a `sendFile` since we would
963957 // have to flush the chunk header before knowing the chunk length.
964958 return error.Unimplemented;
965959 };
......@@ -1003,10 +997,10 @@ pub const BodyWriter = struct {
1003997 }
1004998 }
1005999
1006 fn chunkedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1007 const bw: *BodyWriter = @fieldParentPtr("interface", w);
1000 pub fn chunkedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize {
1001 const bw: *BodyWriter = @fieldParentPtr("writer", w);
10081002 assert(!bw.isEliding());
1009 const out = w.http_protocol_output;
1003 const out = bw.http_protocol_output;
10101004 const data_len = Writer.countSplat(w.end, data, splat);
10111005 const chunked = &bw.state.chunked;
10121006 state: switch (chunked.*) {
......@@ -1018,7 +1012,7 @@ pub const BodyWriter = struct {
10181012 const buffered_len = out.end - offset - chunk_header_template.len;
10191013 const chunk_len = data_len + buffered_len;
10201014 writeHex(out.buffer[offset..][0..chunk_len_digits], chunk_len);
1021 const n = try w.drainTo(w, data, splat);
1015 const n = try w.drainTo(out, data, splat);
10221016 chunked.* = .{ .chunk_len = data_len + 2 - n };
10231017 return n;
10241018 },
......@@ -1041,7 +1035,7 @@ pub const BodyWriter = struct {
10411035 continue :l 1;
10421036 },
10431037 else => {
1044 const n = try w.drainToLimit(data, splat, .limited(chunk_len - 2));
1038 const n = try w.drainToLimit(out, data, splat, .limited(chunk_len - 2));
10451039 chunked.chunk_len = chunk_len - n;
10461040 return n;
10471041 },
lib/std/http/Client.zig+28-26
......@@ -408,7 +408,7 @@ pub const Connection = struct {
408408
409409 /// HTTP protocol from server to client.
410410 /// This either comes directly from `stream_reader`, or from a TLS client.
411 pub fn reader(c: *const Connection) *Reader {
411 pub fn reader(c: *Connection) *Reader {
412412 return switch (c.protocol) {
413413 .tls => {
414414 if (disable_tls) unreachable;
......@@ -682,7 +682,7 @@ pub const Response = struct {
682682 ///
683683 /// See also:
684684 /// * `readerDecompressing`
685 pub fn reader(response: *Response, buffer: []u8) Reader {
685 pub fn reader(response: *Response, buffer: []u8) *Reader {
686686 const req = response.request;
687687 if (!req.method.responseHasBody()) return .ending;
688688 const head = &response.head;
......@@ -702,7 +702,7 @@ pub const Response = struct {
702702 response: *Response,
703703 decompressor: *http.Decompressor,
704704 decompression_buffer: []u8,
705 ) Reader {
705 ) *Reader {
706706 const head = &response.head;
707707 return response.request.reader.bodyReaderDecompressing(
708708 head.transfer_encoding,
......@@ -864,14 +864,14 @@ pub const Request = struct {
864864 pub fn sendBodyUnflushed(r: *Request, buffer: []u8) Writer.Error!http.BodyWriter {
865865 assert(r.method.requestHasBody());
866866 try sendHead(r);
867 const http_protocol_output = &r.connection.?.writer;
867 const http_protocol_output = r.connection.?.writer();
868868 return switch (r.transfer_encoding) {
869869 .chunked => .{
870870 .http_protocol_output = http_protocol_output,
871871 .state = .{ .chunked = .init },
872 .interface = .{
872 .writer = .{
873873 .buffer = buffer,
874 .interface = &.{
874 .vtable = &.{
875875 .drain = http.BodyWriter.chunkedDrain,
876876 .sendFile = http.BodyWriter.chunkedSendFile,
877877 },
......@@ -880,9 +880,9 @@ pub const Request = struct {
880880 .content_length => |len| .{
881881 .http_protocol_output = http_protocol_output,
882882 .state = .{ .content_length = len },
883 .interface = .{
883 .writer = .{
884884 .buffer = buffer,
885 .interface = &.{
885 .vtable = &.{
886886 .drain = http.BodyWriter.contentLengthDrain,
887887 .sendFile = http.BodyWriter.contentLengthSendFile,
888888 },
......@@ -891,9 +891,9 @@ pub const Request = struct {
891891 .none => .{
892892 .http_protocol_output = http_protocol_output,
893893 .state = .none,
894 .interface = .{
894 .writer = .{
895895 .buffer = buffer,
896 .interface = &.{
896 .vtable = &.{
897897 .drain = http.BodyWriter.noneDrain,
898898 .sendFile = http.BodyWriter.noneSendFile,
899899 },
......@@ -906,7 +906,7 @@ pub const Request = struct {
906906 fn sendHead(r: *Request) Writer.Error!void {
907907 const uri = r.uri;
908908 const connection = r.connection.?;
909 const w = &connection.writer;
909 const w = connection.writer();
910910
911911 try r.method.write(w);
912912 try w.writeByte(' ');
......@@ -1085,7 +1085,7 @@ pub const Request = struct {
10851085 if (head.status.class() == .redirect and r.redirect_behavior != .unhandled) {
10861086 if (r.redirect_behavior == .not_allowed) {
10871087 // Connection can still be reused by skipping the body.
1088 var reader = r.reader.bodyReader(head.transfer_encoding, head.content_length);
1088 const reader = r.reader.bodyReader(&.{}, head.transfer_encoding, head.content_length);
10891089 _ = reader.discardRemaining() catch |err| switch (err) {
10901090 error.ReadFailed => connection.closing = true,
10911091 };
......@@ -1117,7 +1117,7 @@ pub const Request = struct {
11171117 {
11181118 // Skip the body of the redirect response to leave the connection in
11191119 // the correct state. This causes `new_location` to be invalidated.
1120 var reader = r.reader.bodyReader(head.transfer_encoding, head.content_length);
1120 const reader = r.reader.bodyReader(&.{}, head.transfer_encoding, head.content_length);
11211121 _ = reader.discardRemaining() catch |err| switch (err) {
11221122 error.ReadFailed => return r.reader.body_err.?,
11231123 };
......@@ -1169,8 +1169,10 @@ pub const Request = struct {
11691169 r.uri = new_uri;
11701170 r.connection = new_connection;
11711171 r.reader = .{
1172 .in = &new_connection.reader,
1172 .in = new_connection.reader(),
11731173 .state = .ready,
1174 // Populated when `http.Reader.bodyReader` is called.
1175 .interface = undefined,
11741176 };
11751177 r.redirect_behavior.subtractOne();
11761178 }
......@@ -1292,12 +1294,12 @@ pub const basic_authorization = struct {
12921294
12931295 pub fn write(uri: Uri, out: *Writer) Writer.Error!void {
12941296 var buf: [max_user_len + ":".len + max_password_len]u8 = undefined;
1295 var bw: Writer = .fixed(&buf);
1296 bw.print("{fuser}:{fpassword}", .{
1297 var w: Writer = .fixed(&buf);
1298 w.print("{fuser}:{fpassword}", .{
12971299 uri.user orelse Uri.Component.empty,
12981300 uri.password orelse Uri.Component.empty,
12991301 }) catch unreachable;
1300 try out.print("Basic {b64}", .{bw.getWritten()});
1302 try out.print("Basic {b64}", .{w.buffered()});
13011303 }
13021304};
13031305
......@@ -1622,8 +1624,10 @@ pub fn request(
16221624 .client = client,
16231625 .connection = connection,
16241626 .reader = .{
1625 .in = &connection.reader,
1627 .in = connection.reader(),
16261628 .state = .ready,
1629 // Populated when `http.Reader.bodyReader` is called.
1630 .interface = undefined,
16271631 },
16281632 .keep_alive = options.keep_alive,
16291633 .method = method,
......@@ -1711,9 +1715,8 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult {
17111715
17121716 if (options.payload) |payload| {
17131717 req.transfer_encoding = .{ .content_length = payload.len };
1714 var body = try req.sendBody();
1715 var bw = body.writer().unbuffered();
1716 try bw.writeAll(payload);
1718 var body = try req.sendBody(&.{});
1719 try body.writer.writeAll(payload);
17171720 try body.end();
17181721 } else {
17191722 try req.sendBodiless();
......@@ -1726,7 +1729,7 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult {
17261729 var response = try req.receiveHead(redirect_buffer);
17271730
17281731 const storage = options.response_storage orelse {
1729 var reader = response.reader();
1732 const reader = response.reader(&.{});
17301733 _ = reader.discardRemaining() catch |err| switch (err) {
17311734 error.ReadFailed => return response.bodyErr().?,
17321735 };
......@@ -1741,18 +1744,17 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult {
17411744 defer if (options.decompress_buffer == null) client.allocator.free(decompress_buffer);
17421745
17431746 var decompressor: http.Decompressor = undefined;
1744 var reader = response.readerDecompressing(&decompressor, decompress_buffer);
1747 const reader = response.readerDecompressing(&decompressor, decompress_buffer);
17451748 const list = storage.list;
17461749
17471750 if (storage.allocator) |allocator| {
1748 reader.readRemainingArrayList(allocator, null, list, storage.append_limit, 128) catch |err| switch (err) {
1751 reader.appendRemaining(allocator, null, list, storage.append_limit) catch |err| switch (err) {
17491752 error.ReadFailed => return response.bodyErr().?,
17501753 else => |e| return e,
17511754 };
17521755 } else {
1753 var br = reader.unbuffered();
17541756 const buf = storage.append_limit.slice(list.unusedCapacitySlice());
1755 list.items.len += br.readSliceShort(buf) catch |err| switch (err) {
1757 list.items.len += reader.readSliceShort(buf) catch |err| switch (err) {
17561758 error.ReadFailed => return response.bodyErr().?,
17571759 };
17581760 }
lib/std/http/Server.zig+17-12
......@@ -26,6 +26,8 @@ pub fn init(in: *std.io.Reader, out: *Writer) Server {
2626 .reader = .{
2727 .in = in,
2828 .state = .ready,
29 // Populated when `http.Reader.bodyReader` is called.
30 .interface = undefined,
2931 },
3032 .out = out,
3133 };
......@@ -58,7 +60,7 @@ pub const Request = struct {
5860 /// Pointers in this struct are invalidated with the next call to
5961 /// `receiveHead`.
6062 head: Head,
61 respond_err: ?RespondError,
63 respond_err: ?RespondError = null,
6264
6365 pub const RespondError = error{
6466 /// The request contained an `expect` header with an unrecognized value.
......@@ -243,6 +245,7 @@ pub const Request = struct {
243245 .in = undefined,
244246 .state = .received_head,
245247 .head_buffer = @constCast(request_bytes),
248 .interface = undefined,
246249 },
247250 .out = undefined,
248251 };
......@@ -381,8 +384,6 @@ pub const Request = struct {
381384 content_length: ?u64 = null,
382385 /// Options that are shared with the `respond` method.
383386 respond_options: RespondOptions = .{},
384 /// Used by `http.BodyWriter`.
385 buffer: []u8,
386387 };
387388
388389 /// The header is not guaranteed to be sent until `BodyWriter.flush` or
......@@ -400,7 +401,11 @@ pub const Request = struct {
400401 /// be done to satisfy the request.
401402 ///
402403 /// Asserts status is not `continue`.
403 pub fn respondStreaming(request: *Request, options: RespondStreamingOptions) Writer.Error!http.BodyWriter {
404 pub fn respondStreaming(
405 request: *Request,
406 buffer: []u8,
407 options: RespondStreamingOptions,
408 ) ExpectContinueError!http.BodyWriter {
404409 try writeExpectContinue(request);
405410 const o = options.respond_options;
406411 assert(o.status != .@"continue");
......@@ -448,12 +453,12 @@ pub const Request = struct {
448453 return if (elide_body) .{
449454 .http_protocol_output = request.server.out,
450455 .state = state,
451 .interface = .discarding(options.buffer),
456 .writer = .discarding(buffer),
452457 } else .{
453458 .http_protocol_output = request.server.out,
454459 .state = state,
455 .interface = .{
456 .buffer = options.buffer,
460 .writer = .{
461 .buffer = buffer,
457462 .vtable = switch (state) {
458463 .none => &.{
459464 .drain = http.BodyWriter.noneDrain,
......@@ -559,11 +564,11 @@ pub const Request = struct {
559564 ///
560565 /// See `readerExpectNone` for an infallible alternative that cannot write
561566 /// to the server output stream.
562 pub fn readerExpectContinue(request: *Request) ExpectContinueError!std.io.Reader {
567 pub fn readerExpectContinue(request: *Request, buffer: []u8) ExpectContinueError!*std.io.Reader {
563568 const flush = request.head.expect != null;
564569 try writeExpectContinue(request);
565570 if (flush) try request.server.out.flush();
566 return readerExpectNone(request);
571 return readerExpectNone(request, buffer);
567572 }
568573
569574 /// Asserts the expect header is `null`. The caller must handle the
......@@ -571,11 +576,11 @@ pub const Request = struct {
571576 /// this function.
572577 ///
573578 /// Asserts that this function is only called once.
574 pub fn readerExpectNone(request: *Request) std.io.Reader {
579 pub fn readerExpectNone(request: *Request, buffer: []u8) *std.io.Reader {
575580 assert(request.server.reader.state == .received_head);
576581 assert(request.head.expect == null);
577582 if (!request.head.method.requestHasBody()) return .ending;
578 return request.server.reader.bodyReader(request.head.transfer_encoding, request.head.content_length);
583 return request.server.reader.bodyReader(buffer, request.head.transfer_encoding, request.head.content_length);
579584 }
580585
581586 pub const ExpectContinueError = error{
......@@ -611,7 +616,7 @@ pub const Request = struct {
611616 .received_head => {
612617 if (request.head.method.requestHasBody()) {
613618 assert(request.head.transfer_encoding != .none or request.head.content_length != null);
614 const reader_interface = request.reader() catch return false;
619 const reader_interface = request.readerExpectContinue(&.{}) catch return false;
615620 _ = reader_interface.discardRemaining() catch return false;
616621 assert(r.state == .ready);
617622 } else {
lib/std/http/test.zig+97-117
......@@ -21,7 +21,7 @@ test "trailers" {
2121
2222 var connection_br = connection.stream.reader(&recv_buffer);
2323 var connection_bw = connection.stream.writer(&send_buffer);
24 var server = http.Server.init(&connection_br, &connection_bw);
24 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
2525
2626 try expectEqual(.ready, server.reader.state);
2727 var request = try server.receiveHead();
......@@ -33,11 +33,10 @@ test "trailers" {
3333 fn serve(request: *http.Server.Request) !void {
3434 try expectEqualStrings(request.head.target, "/trailer");
3535
36 var response = try request.respondStreaming(.{});
37 var bw = response.writer().unbuffered();
38 try bw.writeAll("Hello, ");
36 var response = try request.respondStreaming(&.{}, .{});
37 try response.writer.writeAll("Hello, ");
3938 try response.flush();
40 try bw.writeAll("World!\n");
39 try response.writer.writeAll("World!\n");
4140 try response.flush();
4241 try response.endChunked(.{
4342 .trailers = &.{
......@@ -66,7 +65,7 @@ test "trailers" {
6665 try req.sendBodiless();
6766 var response = try req.receiveHead(&.{});
6867
69 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
68 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
7069 defer gpa.free(body);
7170
7271 try expectEqualStrings("Hello, World!\n", body);
......@@ -104,13 +103,13 @@ test "HTTP server handles a chunked transfer coding request" {
104103
105104 var connection_br = connection.stream.reader(&recv_buffer);
106105 var connection_bw = connection.stream.writer(&send_buffer);
107 var server = http.Server.init(&connection_br, &connection_bw);
106 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
108107 var request = try server.receiveHead();
109108
110109 try expect(request.head.transfer_encoding == .chunked);
111110
112111 var buf: [128]u8 = undefined;
113 var br = (try request.reader()).unbuffered();
112 var br = try request.readerExpectContinue(&.{});
114113 const n = try br.readSliceShort(&buf);
115114 try expectEqualStrings("ABCD", buf[0..n]);
116115
......@@ -141,9 +140,8 @@ test "HTTP server handles a chunked transfer coding request" {
141140 const gpa = std.testing.allocator;
142141 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
143142 defer stream.close();
144 var stream_writer = stream.writer();
145 var writer = stream_writer.interface().unbuffered();
146 try writer.writeAll(request_bytes);
143 var stream_writer = stream.writer(&.{});
144 try stream_writer.interface.writeAll(request_bytes);
147145
148146 const expected_response =
149147 "HTTP/1.1 200 OK\r\n" ++
......@@ -152,8 +150,8 @@ test "HTTP server handles a chunked transfer coding request" {
152150 "content-type: text/plain\r\n" ++
153151 "\r\n" ++
154152 "message from server!\n";
155 var stream_reader = stream.reader();
156 const response = try stream_reader.interface().readRemainingAlloc(gpa, .limited(expected_response.len));
153 var stream_reader = stream.reader(&.{});
154 const response = try stream_reader.interface().allocRemaining(gpa, .limited(expected_response.len));
157155 defer gpa.free(response);
158156 try expectEqualStrings(expected_response, response);
159157}
......@@ -169,11 +167,9 @@ test "echo content server" {
169167 const connection = try net_server.accept();
170168 defer connection.stream.close();
171169
172 var stream_reader = connection.stream.reader();
173 var stream_writer = connection.stream.writer();
174 var connection_br = stream_reader.interface().buffered(&recv_buffer);
175 var connection_bw = stream_writer.interface().buffered(&send_buffer);
176 var http_server = http.Server.init(&connection_br, &connection_bw);
170 var connection_br = connection.stream.reader(&recv_buffer);
171 var connection_bw = connection.stream.writer(&send_buffer);
172 var http_server = http.Server.init(connection_br.interface(), &connection_bw.interface);
177173
178174 while (http_server.reader.state == .ready) {
179175 var request = http_server.receiveHead() catch |err| switch (err) {
......@@ -185,7 +181,7 @@ test "echo content server" {
185181 }
186182 if (request.head.expect) |expect_header_value| {
187183 if (mem.eql(u8, expect_header_value, "garbage")) {
188 try expectError(error.HttpExpectationFailed, request.reader());
184 try expectError(error.HttpExpectationFailed, request.readerExpectContinue(&.{}));
189185 try request.respond("", .{ .keep_alive = false });
190186 continue;
191187 }
......@@ -207,14 +203,14 @@ test "echo content server" {
207203 // request.head.target,
208204 //});
209205
210 const body = try (try request.reader()).readRemainingAlloc(std.testing.allocator, .limited(8192));
206 const body = try (try request.readerExpectContinue(&.{})).allocRemaining(std.testing.allocator, .limited(8192));
211207 defer std.testing.allocator.free(body);
212208
213209 try expect(mem.startsWith(u8, request.head.target, "/echo-content"));
214210 try expectEqualStrings("Hello, World!\n", body);
215211 try expectEqualStrings("text/plain", request.head.content_type.?);
216212
217 var response = try request.respondStreaming(.{
213 var response = try request.respondStreaming(&.{}, .{
218214 .content_length = switch (request.head.transfer_encoding) {
219215 .chunked => null,
220216 .none => len: {
......@@ -224,9 +220,9 @@ test "echo content server" {
224220 },
225221 });
226222 try response.flush(); // Test an early flush to send the HTTP headers before the body.
227 var bw = response.writer().unbuffered();
228 try bw.writeAll("Hello, ");
229 try bw.writeAll("World!\n");
223 const w = &response.writer;
224 try w.writeAll("Hello, ");
225 try w.writeAll("World!\n");
230226 try response.end();
231227 //std.debug.print(" server finished responding\n", .{});
232228 }
......@@ -259,27 +255,25 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
259255 const connection = try net_server.accept();
260256 defer connection.stream.close();
261257
262 var stream_reader = connection.stream.reader();
263 var stream_writer = connection.stream.writer();
264 var connection_br = stream_reader.interface().buffered(&recv_buffer);
265 var connection_bw = stream_writer.interface().buffered(&send_buffer);
266 var server = http.Server.init(&connection_br, &connection_bw);
258 var connection_br = connection.stream.reader(&recv_buffer);
259 var connection_bw = connection.stream.writer(&send_buffer);
260 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
267261
268262 try expectEqual(.ready, server.reader.state);
269263 var request = try server.receiveHead();
270264 try expectEqualStrings(request.head.target, "/foo");
271 var response = try request.respondStreaming(.{
265 var buf: [30]u8 = undefined;
266 var response = try request.respondStreaming(&buf, .{
272267 .respond_options = .{
273268 .transfer_encoding = .none,
274269 },
275270 });
276 var buf: [30]u8 = undefined;
277 var bw = response.writer().buffered(&buf);
271 const w = &response.writer;
278272 for (0..500) |i| {
279 try bw.print("{d}, ah ha ha!\n", .{i});
273 try w.print("{d}, ah ha ha!\n", .{i});
280274 }
281 try expectEqual(7390, bw.count);
282 try bw.flush();
275 try expectEqual(7390, w.count);
276 try w.flush();
283277 try response.end();
284278 try expectEqual(.closing, server.reader.state);
285279 }
......@@ -291,12 +285,11 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
291285 const gpa = std.testing.allocator;
292286 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
293287 defer stream.close();
294 var stream_writer = stream.writer();
295 var writer = stream_writer.interface().unbuffered();
296 try writer.writeAll(request_bytes);
288 var stream_writer = stream.writer(&.{});
289 try stream_writer.interface.writeAll(request_bytes);
297290
298 var stream_reader = stream.reader();
299 const response = try stream_reader.interface().readRemainingAlloc(gpa, .limited(8192));
291 var stream_reader = stream.reader(&.{});
292 const response = try stream_reader.interface().allocRemaining(gpa, .limited(8192));
300293 defer gpa.free(response);
301294
302295 var expected_response = std.ArrayList(u8).init(gpa);
......@@ -329,11 +322,9 @@ test "receiving arbitrary http headers from the client" {
329322 const connection = try net_server.accept();
330323 defer connection.stream.close();
331324
332 var stream_reader = connection.stream.reader();
333 var stream_writer = connection.stream.writer();
334 var connection_br = stream_reader.interface().buffered(&recv_buffer);
335 var connection_bw = stream_writer.interface().buffered(&send_buffer);
336 var server = http.Server.init(&connection_br, &connection_bw);
325 var connection_br = connection.stream.reader(&recv_buffer);
326 var connection_bw = connection.stream.writer(&send_buffer);
327 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
337328
338329 try expectEqual(.ready, server.reader.state);
339330 var request = try server.receiveHead();
......@@ -364,12 +355,11 @@ test "receiving arbitrary http headers from the client" {
364355 const gpa = std.testing.allocator;
365356 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
366357 defer stream.close();
367 var stream_writer = stream.writer();
368 var writer = stream_writer.interface().unbuffered();
369 try writer.writeAll(request_bytes);
358 var stream_writer = stream.writer(&.{});
359 try stream_writer.interface.writeAll(request_bytes);
370360
371 var stream_reader = stream.reader();
372 const response = try stream_reader.interface().readRemainingAlloc(gpa, .limited(8192));
361 var stream_reader = stream.reader(&.{});
362 const response = try stream_reader.interface().allocRemaining(gpa, .limited(8192));
373363 defer gpa.free(response);
374364
375365 var expected_response = std.ArrayList(u8).init(gpa);
......@@ -397,11 +387,9 @@ test "general client/server API coverage" {
397387 var connection = try net_server.accept();
398388 defer connection.stream.close();
399389
400 var stream_reader = connection.stream.reader();
401 var stream_writer = connection.stream.writer();
402 var connection_br = stream_reader.interface().buffered(&recv_buffer);
403 var connection_bw = stream_writer.interface().buffered(&send_buffer);
404 var http_server = http.Server.init(&connection_br, &connection_bw);
390 var connection_br = connection.stream.reader(&recv_buffer);
391 var connection_bw = connection.stream.writer(&send_buffer);
392 var http_server = http.Server.init(connection_br.interface(), &connection_bw.interface);
405393
406394 while (http_server.reader.state == .ready) {
407395 var request = http_server.receiveHead() catch |err| switch (err) {
......@@ -424,11 +412,11 @@ test "general client/server API coverage" {
424412 });
425413
426414 const gpa = std.testing.allocator;
427 const body = try (try request.reader()).readRemainingAlloc(gpa, .limited(8192));
415 const body = try (try request.readerExpectContinue(&.{})).allocRemaining(gpa, .limited(8192));
428416 defer gpa.free(body);
429417
430418 if (mem.startsWith(u8, request.head.target, "/get")) {
431 var response = try request.respondStreaming(.{
419 var response = try request.respondStreaming(&.{}, .{
432420 .content_length = if (mem.indexOf(u8, request.head.target, "?chunked") == null)
433421 14
434422 else
......@@ -439,35 +427,35 @@ test "general client/server API coverage" {
439427 },
440428 },
441429 });
442 var bw = response.writer().unbuffered();
443 try bw.writeAll("Hello, ");
444 try bw.writeAll("World!\n");
430 const w = &response.writer;
431 try w.writeAll("Hello, ");
432 try w.writeAll("World!\n");
445433 try response.end();
446434 // Writing again would cause an assertion failure.
447435 } else if (mem.startsWith(u8, request.head.target, "/large")) {
448 var response = try request.respondStreaming(.{
436 var response = try request.respondStreaming(&.{}, .{
449437 .content_length = 14 * 1024 + 14 * 10,
450438 });
451439
452440 try response.flush(); // Test an early flush to send the HTTP headers before the body.
453441
454 var bw = response.writer().unbuffered();
442 const w = &response.writer;
455443
456444 var i: u32 = 0;
457445 while (i < 5) : (i += 1) {
458 try bw.writeAll("Hello, World!\n");
446 try w.writeAll("Hello, World!\n");
459447 }
460448
461 try bw.writeAll("Hello, World!\n" ** 1024);
449 try w.writeAll("Hello, World!\n" ** 1024);
462450
463451 i = 0;
464452 while (i < 5) : (i += 1) {
465 try bw.writeAll("Hello, World!\n");
453 try w.writeAll("Hello, World!\n");
466454 }
467455
468456 try response.end();
469457 } else if (mem.eql(u8, request.head.target, "/redirect/1")) {
470 var response = try request.respondStreaming(.{
458 var response = try request.respondStreaming(&.{}, .{
471459 .respond_options = .{
472460 .status = .found,
473461 .extra_headers = &.{
......@@ -476,9 +464,9 @@ test "general client/server API coverage" {
476464 },
477465 });
478466
479 var bw = response.writer().unbuffered();
480 try bw.writeAll("Hello, ");
481 try bw.writeAll("Redirected!\n");
467 const w = &response.writer;
468 try w.writeAll("Hello, ");
469 try w.writeAll("Redirected!\n");
482470 try response.end();
483471 } else if (mem.eql(u8, request.head.target, "/redirect/2")) {
484472 try request.respond("Hello, Redirected!\n", .{
......@@ -567,7 +555,7 @@ test "general client/server API coverage" {
567555 try req.sendBodiless();
568556 var response = try req.receiveHead(&redirect_buffer);
569557
570 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
558 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
571559 defer gpa.free(body);
572560
573561 try expectEqualStrings("Hello, World!\n", body);
......@@ -590,7 +578,7 @@ test "general client/server API coverage" {
590578 try req.sendBodiless();
591579 var response = try req.receiveHead(&redirect_buffer);
592580
593 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192 * 1024));
581 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192 * 1024));
594582 defer gpa.free(body);
595583
596584 try expectEqual(@as(usize, 14 * 1024 + 14 * 10), body.len);
......@@ -612,7 +600,7 @@ test "general client/server API coverage" {
612600 try req.sendBodiless();
613601 var response = try req.receiveHead(&redirect_buffer);
614602
615 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
603 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
616604 defer gpa.free(body);
617605
618606 try expectEqualStrings("", body);
......@@ -636,7 +624,7 @@ test "general client/server API coverage" {
636624 try req.sendBodiless();
637625 var response = try req.receiveHead(&redirect_buffer);
638626
639 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
627 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
640628 defer gpa.free(body);
641629
642630 try expectEqualStrings("Hello, World!\n", body);
......@@ -659,7 +647,7 @@ test "general client/server API coverage" {
659647 try req.sendBodiless();
660648 var response = try req.receiveHead(&redirect_buffer);
661649
662 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
650 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
663651 defer gpa.free(body);
664652
665653 try expectEqualStrings("", body);
......@@ -685,7 +673,7 @@ test "general client/server API coverage" {
685673 try req.sendBodiless();
686674 var response = try req.receiveHead(&redirect_buffer);
687675
688 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
676 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
689677 defer gpa.free(body);
690678
691679 try expectEqualStrings("Hello, World!\n", body);
......@@ -714,7 +702,7 @@ test "general client/server API coverage" {
714702
715703 try std.testing.expectEqual(.ok, response.head.status);
716704
717 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
705 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
718706 defer gpa.free(body);
719707
720708 try expectEqualStrings("", body);
......@@ -751,7 +739,7 @@ test "general client/server API coverage" {
751739 try req.sendBodiless();
752740 var response = try req.receiveHead(&redirect_buffer);
753741
754 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
742 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
755743 defer gpa.free(body);
756744
757745 try expectEqualStrings("Hello, World!\n", body);
......@@ -773,7 +761,7 @@ test "general client/server API coverage" {
773761 try req.sendBodiless();
774762 var response = try req.receiveHead(&redirect_buffer);
775763
776 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
764 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
777765 defer gpa.free(body);
778766
779767 try expectEqualStrings("Hello, World!\n", body);
......@@ -795,7 +783,7 @@ test "general client/server API coverage" {
795783 try req.sendBodiless();
796784 var response = try req.receiveHead(&redirect_buffer);
797785
798 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
786 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
799787 defer gpa.free(body);
800788
801789 try expectEqualStrings("Hello, World!\n", body);
......@@ -836,7 +824,7 @@ test "general client/server API coverage" {
836824 try req.sendBodiless();
837825 var response = try req.receiveHead(&redirect_buffer);
838826
839 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
827 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
840828 defer gpa.free(body);
841829
842830 try expectEqualStrings("Encoded redirect successful!\n", body);
......@@ -878,20 +866,18 @@ test "Server streams both reading and writing" {
878866 const connection = try net_server.accept();
879867 defer connection.stream.close();
880868
881 var stream_reader = connection.stream.reader();
882 var stream_writer = connection.stream.writer();
883 var connection_br = stream_reader.interface().buffered(&recv_buffer);
884 var connection_bw = stream_writer.interface().buffered(&send_buffer);
885 var server = http.Server.init(&connection_br, &connection_bw);
869 var connection_br = connection.stream.reader(&recv_buffer);
870 var connection_bw = connection.stream.writer(&send_buffer);
871 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
886872 var request = try server.receiveHead();
887873 var read_buffer: [100]u8 = undefined;
888 var br = (try request.reader()).buffered(&read_buffer);
889 var response = try request.respondStreaming(.{
874 var br = try request.readerExpectContinue(&read_buffer);
875 var response = try request.respondStreaming(&.{}, .{
890876 .respond_options = .{
891877 .transfer_encoding = .none, // Causes keep_alive=false
892878 },
893879 });
894 var bw = response.writer().unbuffered();
880 const w = &response.writer;
895881
896882 while (true) {
897883 try response.flush();
......@@ -901,7 +887,7 @@ test "Server streams both reading and writing" {
901887 };
902888 br.toss(buf.len);
903889 for (buf) |*b| b.* = std.ascii.toUpper(b.*);
904 try bw.writeAll(buf);
890 try w.writeAll(buf);
905891 }
906892 try response.end();
907893 }
......@@ -921,15 +907,14 @@ test "Server streams both reading and writing" {
921907 defer req.deinit();
922908
923909 req.transfer_encoding = .chunked;
924 var body_writer = try req.sendBody();
910 var body_writer = try req.sendBody(&.{});
925911 var response = try req.receiveHead(&redirect_buffer);
926912
927 var w = body_writer.writer().unbuffered();
928 try w.writeAll("one ");
929 try w.writeAll("fish");
913 try body_writer.writer.writeAll("one ");
914 try body_writer.writer.writeAll("fish");
930915 try body_writer.end();
931916
932 const body = try response.reader().readRemainingAlloc(std.testing.allocator, .limited(8192));
917 const body = try response.reader(&.{}).allocRemaining(std.testing.allocator, .limited(8192));
933918 defer std.testing.allocator.free(body);
934919
935920 try expectEqualStrings("ONE FISH", body);
......@@ -954,15 +939,14 @@ fn echoTests(client: *http.Client, port: u16) !void {
954939
955940 req.transfer_encoding = .{ .content_length = 14 };
956941
957 var body_writer = try req.sendBody();
958 var w = body_writer.writer().unbuffered();
959 try w.writeAll("Hello, ");
960 try w.writeAll("World!\n");
942 var body_writer = try req.sendBody(&.{});
943 try body_writer.writer.writeAll("Hello, ");
944 try body_writer.writer.writeAll("World!\n");
961945 try body_writer.end();
962946
963947 var response = try req.receiveHead(&redirect_buffer);
964948
965 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
949 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
966950 defer gpa.free(body);
967951
968952 try expectEqualStrings("Hello, World!\n", body);
......@@ -988,15 +972,14 @@ fn echoTests(client: *http.Client, port: u16) !void {
988972
989973 req.transfer_encoding = .chunked;
990974
991 var body_writer = try req.sendBody();
992 var w = body_writer.writer().unbuffered();
993 try w.writeAll("Hello, ");
994 try w.writeAll("World!\n");
975 var body_writer = try req.sendBody(&.{});
976 try body_writer.writer.writeAll("Hello, ");
977 try body_writer.writer.writeAll("World!\n");
995978 try body_writer.end();
996979
997980 var response = try req.receiveHead(&redirect_buffer);
998981
999 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
982 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
1000983 defer gpa.free(body);
1001984
1002985 try expectEqualStrings("Hello, World!\n", body);
......@@ -1042,16 +1025,15 @@ fn echoTests(client: *http.Client, port: u16) !void {
10421025
10431026 req.transfer_encoding = .chunked;
10441027
1045 var body_writer = try req.sendBody();
1046 var w = body_writer.writer().unbuffered();
1047 try w.writeAll("Hello, ");
1048 try w.writeAll("World!\n");
1028 var body_writer = try req.sendBody(&.{});
1029 try body_writer.writer.writeAll("Hello, ");
1030 try body_writer.writer.writeAll("World!\n");
10491031 try body_writer.end();
10501032
10511033 var response = try req.receiveHead(&redirect_buffer);
10521034 try expectEqual(.ok, response.head.status);
10531035
1054 const body = try response.reader().readRemainingAlloc(gpa, .limited(8192));
1036 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
10551037 defer gpa.free(body);
10561038
10571039 try expectEqualStrings("Hello, World!\n", body);
......@@ -1073,11 +1055,11 @@ fn echoTests(client: *http.Client, port: u16) !void {
10731055
10741056 req.transfer_encoding = .chunked;
10751057
1076 var body_writer = try req.sendBody();
1058 var body_writer = try req.sendBody(&.{});
10771059 try body_writer.flush();
10781060 var response = try req.receiveHead(&redirect_buffer);
10791061 try expectEqual(.expectation_failed, response.head.status);
1080 _ = try response.reader().discardRemaining();
1062 _ = try response.reader(&.{}).discardRemaining();
10811063 }
10821064}
10831065
......@@ -1128,11 +1110,9 @@ test "redirect to different connection" {
11281110 const connection = try net_server.accept();
11291111 defer connection.stream.close();
11301112
1131 var stream_reader = connection.stream.reader();
1132 var stream_writer = connection.stream.writer();
1133 var connection_br = stream_reader.interface().buffered(&recv_buffer);
1134 var connection_bw = stream_writer.interface().buffered(&send_buffer);
1135 var server = http.Server.init(&connection_br, &connection_bw);
1113 var connection_br = connection.stream.reader(&recv_buffer);
1114 var connection_bw = connection.stream.writer(&send_buffer);
1115 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
11361116 var request = try server.receiveHead();
11371117 try expectEqualStrings(request.head.target, "/ok");
11381118 try request.respond("good job, you pass", .{});
......@@ -1161,7 +1141,7 @@ test "redirect to different connection" {
11611141
11621142 var connection_br = connection.stream.reader(&recv_buffer);
11631143 var connection_bw = connection.stream.writer(&send_buffer);
1164 var server = http.Server.init(&connection_br, &connection_bw);
1144 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
11651145 var request = try server.receiveHead();
11661146 try expectEqualStrings(request.head.target, "/help");
11671147 try request.respond("", .{
lib/std/io/Reader.zig+4-1
......@@ -96,7 +96,10 @@ pub const failing: Reader = .{
9696 .end = 0,
9797};
9898
99pub const ending: Reader = .fixed(&.{});
99/// This is generally safe to `@constCast` because it has an empty buffer, so
100/// there is not really a way to accidentally attempt mutation of these fields.
101const ending_state: Reader = .fixed(&.{});
102pub const ending: *Reader = @constCast(&ending_state);
100103
101104pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited {
102105 return Limited.init(r, limit, buffer);
lib/std/io/Writer.zig+79-10
......@@ -221,11 +221,12 @@ pub fn writeSplatLimit(
221221/// `end`.
222222pub fn flush(w: *Writer) Error!void {
223223 assert(0 == try w.vtable.drain(w, &.{}, 0));
224 if (w.end != 0) assert(w.vtable.drain == &fixedDrain);
224225}
225226
226227/// Calls `VTable.drain` but hides the last `preserve_length` bytes from the
227228/// implementation, keeping them buffered.
228pub fn drainLimited(w: *Writer, preserve_length: usize) Error!void {
229pub fn drainPreserve(w: *Writer, preserve_length: usize) Error!void {
229230 const temp_end = w.end -| preserve_length;
230231 const preserved = w.buffer[temp_end..w.end];
231232 w.end = temp_end;
......@@ -235,6 +236,67 @@ pub fn drainLimited(w: *Writer, preserve_length: usize) Error!void {
235236 @memmove(w.buffer[w.end..][0..preserved.len], preserved);
236237}
237238
239/// Forwards a `drain` to a second `Writer` instance. `w` is only used for its
240/// buffer, but it has its `end` and `count` adjusted accordingly depending on
241/// how much was consumed.
242///
243/// Returns how many bytes from `data` were consumed.
244pub fn drainTo(noalias w: *Writer, noalias other: *Writer, data: []const []const u8, splat: usize) Error!usize {
245 assert(w != other);
246 const header = w.buffered();
247 const new_end = other.end + header.len;
248 if (new_end <= other.buffer.len) {
249 @memcpy(other.buffer[other.end..][0..header.len], header);
250 other.end = new_end;
251 other.count += header.len;
252 w.end = 0;
253 const n = try other.vtable.drain(other, data, splat);
254 other.count += n;
255 return n;
256 }
257 if (other.vtable == &VectorWrapper.vtable) {
258 const wrapper: *VectorWrapper = @fieldParentPtr("writer", w);
259 while (wrapper.it.next()) |dest| {
260 _ = dest;
261 @panic("TODO");
262 }
263 }
264 var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size.
265 var i: usize = 1;
266 vecs[0] = header;
267 for (data) |buf| {
268 if (buf.len == 0) continue;
269 vecs[i] = buf;
270 i += 1;
271 if (vecs.len - i == 0) break;
272 }
273 const new_splat = if (vecs[i - 1].ptr == data[data.len - 1].ptr) splat else 1;
274 const n = try other.vtable.drain(other, vecs[0..i], new_splat);
275 other.count += n;
276 if (n < header.len) {
277 const remaining = w.buffer[n..w.end];
278 @memmove(w.buffer[0..remaining.len], remaining);
279 w.end = remaining.len;
280 return 0;
281 }
282 defer w.end = 0;
283 return n - header.len;
284}
285
286pub fn drainToLimit(
287 noalias w: *Writer,
288 noalias other: *Writer,
289 data: []const []const u8,
290 splat: usize,
291 limit: Limit,
292) Error!usize {
293 assert(w != other);
294 _ = data;
295 _ = splat;
296 _ = limit;
297 @panic("TODO");
298}
299
238300pub fn unusedCapacitySlice(w: *const Writer) []u8 {
239301 return w.buffer[w.end..];
240302}
......@@ -285,10 +347,10 @@ pub fn writableSliceGreedy(w: *Writer, minimum_length: usize) Error![]u8 {
285347/// remain buffered.
286348///
287349/// If `preserve_length` is zero, this is equivalent to `writableSliceGreedy`.
288pub fn writableSliceGreedyPreserving(w: *Writer, preserve_length: usize, minimum_length: usize) Error![]u8 {
350pub fn writableSliceGreedyPreserve(w: *Writer, preserve_length: usize, minimum_length: usize) Error![]u8 {
289351 assert(w.buffer.len >= preserve_length + minimum_length);
290352 while (w.buffer.len - w.end < minimum_length) {
291 try drainLimited(w, preserve_length);
353 try drainPreserve(w, preserve_length);
292354 } else {
293355 @branchHint(.likely);
294356 return w.buffer[w.end..];
......@@ -444,7 +506,7 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize {
444506}
445507
446508/// Asserts `buffer` capacity exceeds `preserve_length`.
447pub fn writePreserving(w: *Writer, preserve_length: usize, bytes: []const u8) Error!usize {
509pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Error!usize {
448510 assert(preserve_length <= w.buffer.len);
449511 if (w.end + bytes.len <= w.buffer.len) {
450512 @branchHint(.likely);
......@@ -478,9 +540,9 @@ pub fn writeAll(w: *Writer, bytes: []const u8) Error!void {
478540/// remain buffered.
479541///
480542/// Asserts `buffer` capacity exceeds `preserve_length`.
481pub fn writeAllPreserving(w: *Writer, preserve_length: usize, bytes: []const u8) Error!void {
543pub fn writeAllPreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Error!void {
482544 var index: usize = 0;
483 while (index < bytes.len) index += try w.writePreserving(preserve_length, bytes[index..]);
545 while (index < bytes.len) index += try w.writePreserve(preserve_length, bytes[index..]);
484546}
485547
486548pub fn print(w: *Writer, comptime format: []const u8, args: anytype) Error!void {
......@@ -505,9 +567,9 @@ pub fn writeByte(w: *Writer, byte: u8) Error!void {
505567
506568/// When draining the buffer, ensures that at least `preserve_length` bytes
507569/// remain buffered.
508pub fn writeBytePreserving(w: *Writer, preserve_length: usize, byte: u8) Error!void {
570pub fn writeBytePreserve(w: *Writer, preserve_length: usize, byte: u8) Error!void {
509571 while (w.buffer.len - w.end == 0) {
510 try drainLimited(w, preserve_length);
572 try drainPreserve(w, preserve_length);
511573 } else {
512574 @branchHint(.likely);
513575 w.buffer[w.end] = byte;
......@@ -615,19 +677,26 @@ pub fn sendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!u
615677/// on how much was consumed.
616678///
617679/// Returns how many bytes from `file_reader` were consumed.
618pub fn sendFileTo(w: *Writer, other: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {
680pub fn sendFileTo(
681 noalias w: *Writer,
682 noalias other: *Writer,
683 file_reader: *File.Reader,
684 limit: Limit,
685) FileError!usize {
686 assert(w != other);
619687 const header = w.buffered();
620688 const new_end = other.end + header.len;
621689 if (new_end <= other.buffer.len) {
622690 @memcpy(other.buffer[other.end..][0..header.len], header);
623691 other.end = new_end;
692 other.count += header.len;
624693 w.end = 0;
625694 return other.vtable.sendFile(other, file_reader, limit);
626695 }
627696 assert(header.len > 0);
628697 var vec_buf: [2][]const u8 = .{ header, undefined };
629698 var vec_i: usize = 1;
630 const buffered_contents = limit.slice(file_reader.buffered());
699 const buffered_contents = limit.slice(file_reader.interface.buffered());
631700 if (buffered_contents.len > 0) {
632701 vec_buf[vec_i] = buffered_contents;
633702 vec_i += 1;