diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index 77383ae278e5..4ea8ae379f92 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -271,8 +271,8 @@ fn writeSplatHeaderLimitFinish( if (remaining == 0) break :v; } for (data[0 .. data.len - 1]) |buf| if (buf.len != 0) { - const copy_len = @min(header.len, remaining); - vecs[i] = buf; + const copy_len = @min(buf.len, remaining); + vecs[i] = buf[0..copy_len]; i += 1; remaining -= copy_len; if (remaining == 0) break :v; @@ -306,6 +306,24 @@ test "writeSplatHeader splatting avoids buffer aliasing temptation" { ); } +test "writeSplatHeaderLimit limits data" { + var aw: Writer.Allocating = .init(testing.allocator); + defer aw.deinit(); + + const data = [_][]const u8{ "good", "...not" }; + const limit: usize = 4; + + const n = try aw.writer.writeSplatHeaderLimit( + &.{}, + &data, + 1, + @enumFromInt(limit), + ); + + try testing.expectEqualStrings("good", aw.written()); + try testing.expectEqual(limit, n); +} + /// Drains all remaining buffered data. pub fn flush(w: *Writer) Error!void { return w.vtable.flush(w); diff --git a/lib/std/http/test.zig b/lib/std/http/test.zig index 91a57dbbe2e9..6523126a862a 100644 --- a/lib/std/http/test.zig +++ b/lib/std/http/test.zig @@ -930,6 +930,38 @@ test "Server streams both reading and writing" { try expectEqualStrings("ONE FISH", body); } +test "chunked body writer drains in moderation" { + // Test inspired by a bug in one of Io.Writer's write helpers. + // + // The fix doesn't touch the HTTP code, but the BodyWriter is + // from what I can tell the only affected code in std. + // + // Possibly the cause of https://github.com/ziglang/zig/issues/24944 + // wherein the `zig std` server on Windows, due to lack of sendfile, + // drains the streaming sources tarball by doing vector writes + // of file contents in the chunked body stream. + + var peer = std.Io.Writer.Allocating.init(std.testing.allocator); + defer peer.deinit(); + + const chunk_capacity = 5; + + var bw: std.http.BodyWriter = .{ + .http_protocol_output = &peer.writer, + .state = .{ .chunk_len = chunk_capacity + "\r\n".len }, + .writer = .{ + .buffer = &.{}, + .vtable = &.{ + .drain = std.http.BodyWriter.chunkedDrain, + }, + }, + }; + + _ = try bw.writer.writeVec(&.{ "great", "...not" }); + + try std.testing.expectEqualStrings("great", peer.written()); +} + fn echoTests(client: *http.Client, port: u16) !void { const gpa = std.testing.allocator; var location_buffer: [100]u8 = undefined;