authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-04-21 21:15:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:28-07:00
logd9b9e3c2724a8ea5b66e231c4bd750c03e8f095c
tree80279fee94c7457b594abba2bf53570ed95f7d14
parent4004c8058e2233f1807c0c7f1a5bd4db4558a134

std: update some http to new reader/writer


10 files changed, 222 insertions(+), 122 deletions(-)

lib/std/crypto/Certificate/Bundle.zig+1-1
......@@ -236,7 +236,7 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFrom
236236 try cb.bytes.ensureUnusedCapacity(gpa, needed_capacity);
237237 const end_reserved: u32 = @intCast(cb.bytes.items.len + decoded_size_upper_bound);
238238 const buffer = cb.bytes.allocatedSlice()[end_reserved..];
239 const end_index = try file.readAll(buffer);
239 const end_index = try file.readShort(buffer);
240240 const encoded_bytes = buffer[0..end_index];
241241
242242 const begin_marker = "-----BEGIN CERTIFICATE-----";
lib/std/crypto/tls/Client.zig+1-1
......@@ -916,7 +916,7 @@ pub fn writer(c: *Client) std.io.Writer {
916916 .context = c,
917917 .vtable = &.{
918918 .writeSplat = writeSplat,
919 .writeFile = std.io.Writer.unimplemented_writeFile,
919 .writeFile = std.io.Writer.unimplementedWriteFile,
920920 },
921921 };
922922}
lib/std/fs/File.zig+15-1
......@@ -839,6 +839,20 @@ pub fn read(self: File, buffer: []u8) ReadError!usize {
839839 return posix.read(self.handle, buffer);
840840}
841841
842/// One-shot alternative to `std.io.BufferedReader.readShort` via `reader`.
843///
844/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
845/// means the file reached the end.
846pub fn readShort(self: File, buffer: []u8) ReadError!usize {
847 var index: usize = 0;
848 while (index != buffer.len) {
849 const n = try self.read(buffer[index..]);
850 if (n == 0) break;
851 index += n;
852 }
853 return index;
854}
855
842856/// On Windows, this function currently does alter the file pointer.
843857/// https://github.com/ziglang/zig/issues/12783
844858pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
......@@ -884,7 +898,7 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize {
884898 return posix.write(self.handle, bytes);
885899}
886900
887/// One-shot alternative to `writer`.
901/// One-shot alternative to `std.io.BufferedWriter.writeAll` via `writer`.
888902pub fn writeAll(self: File, bytes: []const u8) WriteError!void {
889903 var index: usize = 0;
890904 while (index < bytes.len) {
lib/std/http/Client.zig+13-14
......@@ -38,9 +38,12 @@ next_https_rescan_certs: bool = true,
3838/// The pool of connections that can be reused (and currently in use).
3939connection_pool: ConnectionPool = .{},
4040/// Each `Connection` allocates this amount for the reader buffer.
41read_buffer_size: usize,
41///
42/// If the entire HTTP header cannot fit in this amount of bytes,
43/// `error.HttpHeadersOversize` will be returned from `Request.wait`.
44read_buffer_size: usize = 4096,
4245/// Each `Connection` allocates this amount for the writer buffer.
43write_buffer_size: usize,
46write_buffer_size: usize = 1024,
4447
4548/// If populated, all http traffic travels through this third party.
4649/// This field cannot be modified while the client has active connections.
......@@ -177,23 +180,21 @@ pub const ConnectionPool = struct {
177180 /// All future operations on the connection pool will deadlock.
178181 ///
179182 /// Threadsafe.
180 pub fn deinit(pool: *ConnectionPool, allocator: Allocator) void {
183 pub fn deinit(pool: *ConnectionPool) void {
181184 pool.mutex.lock();
182185
183186 var next = pool.free.first;
184187 while (next) |node| {
185188 const connection: *Connection = @fieldParentPtr("pool_node", node);
186189 next = node.next;
187 connection.close(allocator);
188 allocator.destroy(connection);
190 connection.destroy();
189191 }
190192
191193 next = pool.used.first;
192194 while (next) |node| {
193195 const connection: *Connection = @fieldParentPtr("pool_node", node);
194196 next = node.next;
195 connection.close(allocator);
196 allocator.destroy(node);
197 connection.destroy();
197198 }
198199
199200 pool.* = undefined;
......@@ -324,8 +325,9 @@ pub const Connection = struct {
324325 return tls;
325326 }
326327
327 fn destroy(tls: *Tls, gpa: Allocator) void {
328 fn destroy(tls: *Tls) void {
328329 const c = &tls.connection;
330 const gpa = c.client.allocator;
329331 const base: [*]u8 = @ptrCast(tls);
330332 gpa.free(base[0..allocLen(c.client, c.host_len)]);
331333 }
......@@ -1262,10 +1264,8 @@ pub const Proxy = struct {
12621264pub fn deinit(client: *Client) void {
12631265 assert(client.connection_pool.used.first == null); // There are still active requests.
12641266
1265 client.connection_pool.deinit(client.allocator);
1266
1267 if (!disable_tls)
1268 client.ca_bundle.deinit(client.allocator);
1267 client.connection_pool.deinit();
1268 if (!disable_tls) client.ca_bundle.deinit(client.allocator);
12691269
12701270 client.* = undefined;
12711271}
......@@ -1574,8 +1574,7 @@ pub fn connect(
15741574
15751575/// TODO collapse each error set into its own meta error code, and store
15761576/// the underlying error code as a field on Request
1577pub const RequestError = ConnectTcpError || ConnectErrorPartial || std.io.Writer.Error ||
1578 std.fmt.ParseIntError || Connection.WriteError ||
1577pub const RequestError = ConnectTcpError || ConnectErrorPartial || std.io.Writer.Error || std.fmt.ParseIntError ||
15791578 error{
15801579 UnsupportedUriScheme,
15811580 UriMissingHost,
lib/std/http/Server.zig+34-14
......@@ -20,7 +20,8 @@ out: *std.io.BufferedWriter,
2020/// same connection, and makes invalid API usage cause assertion failures
2121/// rather than HTTP protocol violations.
2222state: State,
23head_parse_err: Request.Head.ParseError,
23/// Populated when `receiveHead` returns `ReceiveHeadError.HttpHeadersInvalid`.
24head_parse_err: ?Request.Head.ParseError = null,
2425
2526pub const State = enum {
2627 /// The connection is available to be used for the first time, or reused.
......@@ -46,7 +47,6 @@ pub fn init(in: *std.io.BufferedReader, out: *std.io.BufferedWriter) Server {
4647 .in = in,
4748 .out = out,
4849 .state = .ready,
49 .head_parse_err = undefined,
5050 };
5151}
5252
......@@ -92,8 +92,6 @@ pub fn receiveHead(s: *Server) ReceiveHeadError!Request {
9292 if (hp.state == .finished) return .{
9393 .server = s,
9494 .head_end = head_end,
95 .trailers_len = 0,
96 .read_err = null,
9795 .head = Request.Head.parse(buf[0..head_end]) catch |err| {
9896 s.head_parse_err = err;
9997 return error.HttpHeadersInvalid;
......@@ -109,13 +107,13 @@ pub const Request = struct {
109107 head_end: usize,
110108 /// Number of bytes of HTTP trailers. These are at the end of a
111109 /// transfer-encoding: chunked message.
112 trailers_len: usize,
110 trailers_len: usize = 0,
113111 head: Head,
114112 reader_state: union {
115113 remaining_content_length: u64,
116114 remaining_chunk_len: RemainingChunkLen,
117115 },
118 read_err: ?ReadError,
116 read_err: ?ReadError = null,
119117
120118 pub const ReadError = error{
121119 HttpChunkInvalid,
......@@ -330,12 +328,12 @@ pub const Request = struct {
330328 .in = &br,
331329 .out = undefined,
332330 .state = .ready,
333 .in_err = undefined,
334331 };
335332
336333 var request: Request = .{
337334 .server = &server,
338335 .head_end = request_bytes.len,
336 .trailers_len = 0,
339337 .head = undefined,
340338 .reader_state = undefined,
341339 };
......@@ -513,15 +511,16 @@ pub const Request = struct {
513511 respond_options: RespondOptions = .{},
514512 };
515513
516 /// The header is buffered but not sent until `Response.flush` is called.
514 /// The header is not guaranteed to be sent until `Response.flush` is called.
517515 ///
518516 /// If the request contains a body and the connection is to be reused,
519517 /// discards the request body, leaving the Server in the `ready` state. If
520518 /// this discarding fails, the connection is marked as not to be reused and
521519 /// no error is surfaced.
522520 ///
523 /// HEAD requests are handled transparently by setting a flag on the
524 /// returned Response to omit the body. However it may be worth noticing
521 /// HEAD requests are handled transparently by setting the
522 /// `Response.elide_body` flag on the returned `Response`, causing
523 /// the response stream to omit the body. However, it may be worth noticing
525524 /// that flag and skipping any expensive work that would otherwise need to
526525 /// be done to satisfy the request.
527526 ///
......@@ -922,6 +921,9 @@ pub const Response = struct {
922921 ///
923922 /// This is the underlying stream; use `buffered` to create a
924923 /// `BufferedWriter` for this `Response`.
924 ///
925 /// Until the lifetime of `Response` ends, it is illegal to modify the
926 /// state of this other than via methods of `Response`.
925927 server_output: *std.io.BufferedWriter,
926928 /// `null` means transfer-encoding: chunked.
927929 /// As a debugging utility, counts down to zero as bytes are written.
......@@ -966,12 +968,30 @@ pub const Response = struct {
966968 };
967969 };
968970
971 /// Sends all buffered data across `Response.server_output`.
972 ///
973 /// Some buffered data will remain if transfer-encoding is chunked and the
974 /// response is mid-chunk.
975 pub fn flush(r: *Response) WriteError!void {
976 switch (r.transfer_encoding) {
977 .none, .content_length => return r.server_output.flush(),
978 .chunked => |*chunked| switch (chunked.*) {
979 .offset => |*offset| {
980 try r.server_output.flushLimit(.limited(r.server_output.end - offset.*));
981 offset.* = 0;
982 },
983 .chunk_len => return r.server_output.flush(),
984 },
985 }
986 }
987
969988 /// When using content-length, asserts that the amount of data sent matches
970 /// the value sent in the header, then calls `flush`.
989 /// the value sent in the header, then flushes. Asserts the amount of bytes
990 /// sent matches the content-length value provided in the HTTP header.
971991 ///
972 /// Otherwise, transfer-encoding: chunked is being used, and it writes the
973 /// end-of-stream message with empty trailers, then flushes the stream to
974 /// the system.
992 /// When using transfer-encoding: chunked, writes the end-of-stream message
993 /// with empty trailers, then flushes the stream to the system. Asserts any
994 /// started chunk has been completely finished.
975995 ///
976996 /// Respects the value of `elide_body` to omit all data after the headers.
977997 ///
lib/std/http/test.zig+121-88
......@@ -11,13 +11,18 @@ const expectError = std.testing.expectError;
1111test "trailers" {
1212 const test_server = try createTestServer(struct {
1313 fn run(net_server: *std.net.Server) anyerror!void {
14 var header_buffer: [1024]u8 = undefined;
14 var recv_buffer: [1024]u8 = undefined;
15 var send_buffer: [1024]u8 = undefined;
1516 var remaining: usize = 1;
1617 while (remaining != 0) : (remaining -= 1) {
17 const conn = try net_server.accept();
18 defer conn.stream.close();
18 const connection = try net_server.accept();
19 defer connection.stream.close();
1920
20 var server = http.Server.init(conn, &header_buffer);
21 var stream_reader = connection.stream.reader();
22 var stream_writer = connection.stream.writer();
23 var connection_br = stream_reader.interface().buffered(&recv_buffer);
24 var connection_bw = stream_writer.interface().buffered(&send_buffer);
25 var server = http.Server.init(&connection_br, &connection_bw);
2126
2227 try expectEqual(.ready, server.state);
2328 var request = try server.receiveHead();
......@@ -29,13 +34,11 @@ test "trailers" {
2934 fn serve(request: *http.Server.Request) !void {
3035 try expectEqualStrings(request.head.target, "/trailer");
3136
32 var send_buffer: [1024]u8 = undefined;
33 var response = request.respondStreaming(.{
34 .send_buffer = &send_buffer,
35 });
36 try response.writeAll("Hello, ");
37 var response = try request.respondStreaming(.{});
38 var bw = response.writer().unbuffered();
39 try bw.writeAll("Hello, ");
3740 try response.flush();
38 try response.writeAll("World!\n");
41 try bw.writeAll("World!\n");
3942 try response.flush();
4043 try response.endChunked(.{
4144 .trailers = &.{
......@@ -95,11 +98,16 @@ test "trailers" {
9598test "HTTP server handles a chunked transfer coding request" {
9699 const test_server = try createTestServer(struct {
97100 fn run(net_server: *std.net.Server) !void {
98 var header_buffer: [8192]u8 = undefined;
99 const conn = try net_server.accept();
100 defer conn.stream.close();
101
102 var server = http.Server.init(conn, &header_buffer);
101 var recv_buffer: [8192]u8 = undefined;
102 var send_buffer: [500]u8 = undefined;
103 const connection = try net_server.accept();
104 defer connection.stream.close();
105
106 var stream_reader = connection.stream.reader();
107 var stream_writer = connection.stream.writer();
108 var connection_br = stream_reader.interface().buffered(&recv_buffer);
109 var connection_bw = stream_writer.interface().buffered(&send_buffer);
110 var server = http.Server.init(&connection_br, &connection_bw);
103111 var request = try server.receiveHead();
104112
105113 try expect(request.head.transfer_encoding == .chunked);
......@@ -153,13 +161,18 @@ test "HTTP server handles a chunked transfer coding request" {
153161test "echo content server" {
154162 const test_server = try createTestServer(struct {
155163 fn run(net_server: *std.net.Server) anyerror!void {
156 var read_buffer: [1024]u8 = undefined;
164 var recv_buffer: [1024]u8 = undefined;
165 var send_buffer: [100]u8 = undefined;
157166
158167 accept: while (true) {
159 const conn = try net_server.accept();
160 defer conn.stream.close();
168 const connection = try net_server.accept();
169 defer connection.stream.close();
161170
162 var http_server = http.Server.init(conn, &read_buffer);
171 var stream_reader = connection.stream.reader();
172 var stream_writer = connection.stream.writer();
173 var connection_br = stream_reader.interface().buffered(&recv_buffer);
174 var connection_bw = stream_writer.interface().buffered(&send_buffer);
175 var http_server = http.Server.init(&connection_br, &connection_bw);
163176
164177 while (http_server.state == .ready) {
165178 var request = http_server.receiveHead() catch |err| switch (err) {
......@@ -200,9 +213,7 @@ test "echo content server" {
200213 try expectEqualStrings("Hello, World!\n", body);
201214 try expectEqualStrings("text/plain", request.head.content_type.?);
202215
203 var send_buffer: [100]u8 = undefined;
204 var response = request.respondStreaming(.{
205 .send_buffer = &send_buffer,
216 var response = try request.respondStreaming(.{
206217 .content_length = switch (request.head.transfer_encoding) {
207218 .chunked => null,
208219 .none => len: {
......@@ -211,11 +222,10 @@ test "echo content server" {
211222 },
212223 },
213224 });
214
215225 try response.flush(); // Test an early flush to send the HTTP headers before the body.
216 const w = response.writer();
217 try w.writeAll("Hello, ");
218 try w.writeAll("World!\n");
226 var bw = response.writer().unbuffered();
227 try bw.writeAll("Hello, ");
228 try bw.writeAll("World!\n");
219229 try response.end();
220230 //std.debug.print(" server finished responding\n", .{});
221231 }
......@@ -240,32 +250,33 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
240250 // closed, indicating the end of the body.
241251 const test_server = try createTestServer(struct {
242252 fn run(net_server: *std.net.Server) anyerror!void {
243 var header_buffer: [1000]u8 = undefined;
253 var recv_buffer: [1000]u8 = undefined;
254 var send_buffer: [500]u8 = undefined;
244255 var remaining: usize = 1;
245256 while (remaining != 0) : (remaining -= 1) {
246 const conn = try net_server.accept();
247 defer conn.stream.close();
257 const connection = try net_server.accept();
258 defer connection.stream.close();
248259
249 var server = http.Server.init(conn, &header_buffer);
260 var stream_reader = connection.stream.reader();
261 var stream_writer = connection.stream.writer();
262 var connection_br = stream_reader.interface().buffered(&recv_buffer);
263 var connection_bw = stream_writer.interface().buffered(&send_buffer);
264 var server = http.Server.init(&connection_br, &connection_bw);
250265
251266 try expectEqual(.ready, server.state);
252267 var request = try server.receiveHead();
253268 try expectEqualStrings(request.head.target, "/foo");
254 var send_buffer: [500]u8 = undefined;
255 var response = request.respondStreaming(.{
256 .send_buffer = &send_buffer,
269 var response = try request.respondStreaming(.{
257270 .respond_options = .{
258271 .transfer_encoding = .none,
259272 },
260273 });
261 var total: usize = 0;
274 var buf: [30]u8 = undefined;
275 var bw = response.writer().buffered(&buf);
262276 for (0..500) |i| {
263 var buf: [30]u8 = undefined;
264 const line = try std.fmt.bufPrint(&buf, "{d}, ah ha ha!\n", .{i});
265 try response.writeAll(line);
266 total += line.len;
277 try bw.print("{d}, ah ha ha!\n", .{i});
267278 }
268 try expectEqual(7390, total);
279 try expectEqual(7390, bw.count);
269280 try response.end();
270281 try expectEqual(.closing, server.state);
271282 }
......@@ -305,13 +316,19 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
305316test "receiving arbitrary http headers from the client" {
306317 const test_server = try createTestServer(struct {
307318 fn run(net_server: *std.net.Server) anyerror!void {
308 var read_buffer: [666]u8 = undefined;
319 var recv_buffer: [666]u8 = undefined;
320 var send_buffer: [777]u8 = undefined;
309321 var remaining: usize = 1;
310322 while (remaining != 0) : (remaining -= 1) {
311 const conn = try net_server.accept();
312 defer conn.stream.close();
323 const connection = try net_server.accept();
324 defer connection.stream.close();
325
326 var stream_reader = connection.stream.reader();
327 var stream_writer = connection.stream.writer();
328 var connection_br = stream_reader.interface().buffered(&recv_buffer);
329 var connection_bw = stream_writer.interface().buffered(&send_buffer);
330 var server = http.Server.init(&connection_br, &connection_bw);
313331
314 var server = http.Server.init(conn, &read_buffer);
315332 try expectEqual(.ready, server.state);
316333 var request = try server.receiveHead();
317334 try expectEqualStrings("/bar", request.head.target);
......@@ -341,7 +358,8 @@ test "receiving arbitrary http headers from the client" {
341358 const gpa = std.testing.allocator;
342359 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
343360 defer stream.close();
344 var writer = stream.writer().unbuffered();
361 var stream_writer = stream.writer();
362 var writer = stream_writer.interface().unbuffered();
345363 try writer.writeAll(request_bytes);
346364
347365 const response = try stream.reader().readAllAlloc(gpa, 8192);
......@@ -367,12 +385,18 @@ test "general client/server API coverage" {
367385 };
368386 const test_server = try createTestServer(struct {
369387 fn run(net_server: *std.net.Server) anyerror!void {
370 var client_header_buffer: [1024]u8 = undefined;
388 var recv_buffer: [1024]u8 = undefined;
389 var send_buffer: [100]u8 = undefined;
390
371391 outer: while (global.handle_new_requests) {
372392 var connection = try net_server.accept();
373393 defer connection.stream.close();
374394
375 var http_server = http.Server.init(connection, &client_header_buffer);
395 var stream_reader = connection.stream.reader();
396 var stream_writer = connection.stream.writer();
397 var connection_br = stream_reader.interface().buffered(&recv_buffer);
398 var connection_bw = stream_writer.interface().buffered(&send_buffer);
399 var http_server = http.Server.init(&connection_br, &connection_bw);
376400
377401 while (http_server.state == .ready) {
378402 var request = http_server.receiveHead() catch |err| switch (err) {
......@@ -398,11 +422,8 @@ test "general client/server API coverage" {
398422 const body = try (try request.reader()).readAllAlloc(gpa, 8192);
399423 defer gpa.free(body);
400424
401 var send_buffer: [100]u8 = undefined;
402
403425 if (mem.startsWith(u8, request.head.target, "/get")) {
404 var response = request.respondStreaming(.{
405 .send_buffer = &send_buffer,
426 var response = try request.respondStreaming(.{
406427 .content_length = if (mem.indexOf(u8, request.head.target, "?chunked") == null)
407428 14
408429 else
......@@ -413,37 +434,35 @@ test "general client/server API coverage" {
413434 },
414435 },
415436 });
416 const w = response.writer();
417 try w.writeAll("Hello, ");
418 try w.writeAll("World!\n");
437 var bw = response.writer().unbuffered();
438 try bw.writeAll("Hello, ");
439 try bw.writeAll("World!\n");
419440 try response.end();
420441 // Writing again would cause an assertion failure.
421442 } else if (mem.startsWith(u8, request.head.target, "/large")) {
422 var response = request.respondStreaming(.{
423 .send_buffer = &send_buffer,
443 var response = try request.respondStreaming(.{
424444 .content_length = 14 * 1024 + 14 * 10,
425445 });
426446
427447 try response.flush(); // Test an early flush to send the HTTP headers before the body.
428448
429 const w = response.writer();
449 var bw = response.writer().unbuffered();
430450
431451 var i: u32 = 0;
432452 while (i < 5) : (i += 1) {
433 try w.writeAll("Hello, World!\n");
453 try bw.writeAll("Hello, World!\n");
434454 }
435455
436 try w.writeAll("Hello, World!\n" ** 1024);
456 try bw.writeAll("Hello, World!\n" ** 1024);
437457
438458 i = 0;
439459 while (i < 5) : (i += 1) {
440 try w.writeAll("Hello, World!\n");
460 try bw.writeAll("Hello, World!\n");
441461 }
442462
443463 try response.end();
444464 } else if (mem.eql(u8, request.head.target, "/redirect/1")) {
445 var response = request.respondStreaming(.{
446 .send_buffer = &send_buffer,
465 var response = try request.respondStreaming(.{
447466 .respond_options = .{
448467 .status = .found,
449468 .extra_headers = &.{
......@@ -452,9 +471,9 @@ test "general client/server API coverage" {
452471 },
453472 });
454473
455 const w = response.writer();
456 try w.writeAll("Hello, ");
457 try w.writeAll("Redirected!\n");
474 var bw = response.writer().unbuffered();
475 try bw.writeAll("Hello, ");
476 try bw.writeAll("Redirected!\n");
458477 try response.end();
459478 } else if (mem.eql(u8, request.head.target, "/redirect/2")) {
460479 try request.respond("Hello, Redirected!\n", .{
......@@ -914,31 +933,36 @@ test "general client/server API coverage" {
914933test "Server streams both reading and writing" {
915934 const test_server = try createTestServer(struct {
916935 fn run(net_server: *std.net.Server) anyerror!void {
917 var header_buffer: [1024]u8 = undefined;
918 const conn = try net_server.accept();
919 defer conn.stream.close();
936 var recv_buffer: [1024]u8 = undefined;
937 var send_buffer: [777]u8 = undefined;
920938
921 var server = http.Server.init(conn, &header_buffer);
922 var request = try server.receiveHead();
923 const reader = try request.reader();
939 const connection = try net_server.accept();
940 defer connection.stream.close();
924941
925 var send_buffer: [777]u8 = undefined;
926 var response = request.respondStreaming(.{
927 .send_buffer = &send_buffer,
942 var stream_reader = connection.stream.reader();
943 var stream_writer = connection.stream.writer();
944 var connection_br = stream_reader.interface().buffered(&recv_buffer);
945 var connection_bw = stream_writer.interface().buffered(&send_buffer);
946 var server = http.Server.init(&connection_br, &connection_bw);
947 var request = try server.receiveHead();
948 var read_buffer: [100]u8 = undefined;
949 var br = try request.reader().buffered(&read_buffer);
950 var response = try request.respondStreaming(.{
928951 .respond_options = .{
929952 .transfer_encoding = .none, // Causes keep_alive=false
930953 },
931954 });
932 const writer = response.writer();
955 var bw = response.writer().unbuffered();
933956
934957 while (true) {
935958 try response.flush();
936 var buf: [100]u8 = undefined;
937 const n = try reader.read(&buf);
938 if (n == 0) break;
939 const sub_buf = buf[0..n];
940 for (sub_buf) |*b| b.* = std.ascii.toUpper(b.*);
941 try writer.writeAll(sub_buf);
959 const buf = br.peekGreedy(1) catch |err| switch (err) {
960 error.EndOfStream => break,
961 error.ReadFailed => return error.ReadFailed,
962 };
963 br.toss(buf.len);
964 for (buf) |*b| b.* = std.ascii.toUpper(b.*);
965 try bw.writeAll(buf);
942966 }
943967 try response.end();
944968 }
......@@ -1161,12 +1185,17 @@ fn createTestServer(S: type) !*TestServer {
11611185test "redirect to different connection" {
11621186 const test_server_new = try createTestServer(struct {
11631187 fn run(net_server: *std.net.Server) anyerror!void {
1164 var header_buffer: [888]u8 = undefined;
1188 var recv_buffer: [888]u8 = undefined;
1189 var send_buffer: [777]u8 = undefined;
11651190
1166 const conn = try net_server.accept();
1167 defer conn.stream.close();
1191 const connection = try net_server.accept();
1192 defer connection.stream.close();
11681193
1169 var server = http.Server.init(conn, &header_buffer);
1194 var stream_reader = connection.stream.reader();
1195 var stream_writer = connection.stream.writer();
1196 var connection_br = stream_reader.interface().buffered(&recv_buffer);
1197 var connection_bw = stream_writer.interface().buffered(&send_buffer);
1198 var server = http.Server.init(&connection_br, &connection_bw);
11701199 var request = try server.receiveHead();
11711200 try expectEqualStrings(request.head.target, "/ok");
11721201 try request.respond("good job, you pass", .{});
......@@ -1181,17 +1210,21 @@ test "redirect to different connection" {
11811210
11821211 const test_server_orig = try createTestServer(struct {
11831212 fn run(net_server: *std.net.Server) anyerror!void {
1184 var header_buffer: [999]u8 = undefined;
1213 var recv_buffer: [999]u8 = undefined;
11851214 var send_buffer: [100]u8 = undefined;
11861215
1187 const conn = try net_server.accept();
1188 defer conn.stream.close();
1216 const connection = try net_server.accept();
1217 defer connection.stream.close();
11891218
11901219 const new_loc = try std.fmt.bufPrint(&send_buffer, "http://127.0.0.1:{d}/ok", .{
11911220 global.other_port.?,
11921221 });
11931222
1194 var server = http.Server.init(conn, &header_buffer);
1223 var stream_reader = connection.stream.reader();
1224 var stream_writer = connection.stream.writer();
1225 var connection_br = stream_reader.interface().buffered(&recv_buffer);
1226 var connection_bw = stream_writer.interface().buffered(&send_buffer);
1227 var server = http.Server.init(&connection_br, &connection_bw);
11951228 var request = try server.receiveHead();
11961229 try expectEqualStrings(request.head.target, "/help");
11971230 try request.respond("", .{
lib/std/io/BufferedReader.zig+2
......@@ -227,6 +227,8 @@ pub fn peekGreedy(br: *BufferedReader, n: usize) Reader.Error![]u8 {
227227///
228228/// Asserts that the number of bytes buffered is at least as many as `n`.
229229///
230/// The "tossed" memory remains alive until a "peek" operation occurs.
231///
230232/// See also:
231233/// * `peek`.
232234/// * `discard`.
lib/std/io/BufferedWriter.zig+13
......@@ -78,6 +78,15 @@ pub fn flush(bw: *BufferedWriter) Writer.Error!void {
7878 bw.end = 0;
7979}
8080
81pub fn flushLimit(bw: *BufferedWriter, limit: Writer.Limit) Writer.Error!void {
82 const buffer = limit.slice(bw.buffer[0..bw.end]);
83 var index: usize = 0;
84 while (index < buffer.len) index += try bw.unbuffered_writer.writeVec(&.{buffer[index..]});
85 const remainder = bw.buffer[index..];
86 std.mem.copyForwards(u8, bw.buffer[0..remainder.len], remainder);
87 bw.end = remainder.len;
88}
89
8190pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {
8291 return bw.buffer[bw.end..];
8392}
......@@ -1852,3 +1861,7 @@ test "fixed output" {
18521861 try bw.seekTo((try bw.getEndPos()) + 1);
18531862 try testing.expectError(error.WriteStreamEnd, bw.writeAll("H"));
18541863}
1864
1865test flushLimit {
1866 return error.Unimplemented;
1867}
lib/std/io/Writer.zig+17
......@@ -156,6 +156,23 @@ pub const failing: Writer = .{
156156 },
157157};
158158
159pub fn unimplementedWriteFile(
160 context: ?*anyopaque,
161 file: std.fs.File,
162 offset: std.io.Writer.Offset,
163 limit: std.io.Writer.Limit,
164 headers_and_trailers: []const []const u8,
165 headers_len: usize,
166) Error!usize {
167 _ = context;
168 _ = file;
169 _ = offset;
170 _ = limit;
171 _ = headers_and_trailers;
172 _ = headers_len;
173 return error.Unimplemented;
174}
175
159176test {
160177 _ = Null;
161178}
lib/std/net.zig+5-3
......@@ -1357,7 +1357,8 @@ fn linuxLookupNameFromHosts(
13571357 defer file.close();
13581358
13591359 var line_buf: [512]u8 = undefined;
1360 var br = file.reader().buffered(&line_buf);
1360 var file_reader = file.reader();
1361 var br = file_reader.interface().buffered(&line_buf);
13611362 while (br.takeSentinel('\n')) |line| {
13621363 var split_it = mem.splitScalar(u8, line, '#');
13631364 const no_comment_line = split_it.first();
......@@ -1530,7 +1531,7 @@ const ResolvConf = struct {
15301531/// Returns `error.StreamTooLong` if a line is longer than 512 bytes.
15311532/// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761
15321533fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void {
1533 rc.* = ResolvConf{
1534 rc.* = .{
15341535 .ns = std.ArrayList(LookupAddr).init(allocator),
15351536 .search = std.ArrayList(u8).init(allocator),
15361537 .ndots = 1,
......@@ -1549,7 +1550,8 @@ fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void {
15491550 defer file.close();
15501551
15511552 var line_buf: [512]u8 = undefined;
1552 var br = file.reader().buffered(&line_buf);
1553 var file_reader = file.reader();
1554 var br = file_reader.interface().buffered(&line_buf);
15531555 while (br.takeSentinel('\n')) |line_with_comment| {
15541556 const line = line: {
15551557 var split = mem.splitScalar(u8, line_with_comment, '#');