authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-11 22:19:41-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-23 02:37:11-07:00
log50e2a5f673d880515caf3497bfb6eb0114149de1
tree81e3aa0e6078f86f4669c3f2189dd54431938da2
parent06d0c58305a04d28da9a2caf510fc906e38a54ef

std.http: remove 'done' flag

This is a state machine that already has a `state` field. No need to additionally store "done" - it just makes things unnecessarily complicated and buggy.

4 files changed, 152 insertions(+), 85 deletions(-)

lib/std/http/Client.zig+12-10
......@@ -610,7 +610,7 @@ pub const Request = struct {
610610 req.response.headers.deinit();
611611
612612 if (req.connection) |connection| {
613 if (!req.response.parser.done) {
613 if (req.response.parser.state != .complete) {
614614 // If the response wasn't fully read, then we need to close the connection.
615615 connection.closing = true;
616616 }
......@@ -624,7 +624,7 @@ pub const Request = struct {
624624 // This function must deallocate all resources associated with the request, or keep those which will be used
625625 // This needs to be kept in sync with deinit and request
626626 fn redirect(req: *Request, uri: Uri) !void {
627 assert(req.response.parser.done);
627 assert(req.response.parser.state == .complete);
628628
629629 switch (req.response.compression) {
630630 .none => {},
......@@ -794,12 +794,12 @@ pub const Request = struct {
794794 }
795795
796796 fn transferRead(req: *Request, buf: []u8) TransferReadError!usize {
797 if (req.response.parser.done) return 0;
797 if (req.response.parser.state == .complete) return 0;
798798
799799 var index: usize = 0;
800800 while (index == 0) {
801801 const amt = try req.response.parser.read(req.connection.?, buf[index..], req.response.skip);
802 if (amt == 0 and req.response.parser.done) break;
802 if (amt == 0 and req.response.parser.state == .complete) break;
803803 index += amt;
804804 }
805805
......@@ -840,7 +840,7 @@ pub const Request = struct {
840840 try req.response.parse(req.response.parser.get(), false);
841841
842842 if (req.response.status == .@"continue") {
843 req.response.parser.done = true; // we're done parsing the continue response, reset to prepare for the real response
843 req.response.parser.state = .complete; // we're done parsing the continue response, reset to prepare for the real response
844844 req.response.parser.reset();
845845
846846 if (req.handle_continue)
......@@ -852,7 +852,7 @@ pub const Request = struct {
852852 // we're switching protocols, so this connection is no longer doing http
853853 if (req.method == .CONNECT and req.response.status.class() == .success) {
854854 req.connection.?.closing = false;
855 req.response.parser.done = true;
855 req.response.parser.state = .complete;
856856
857857 return; // the connection is not HTTP past this point, return to the caller
858858 }
......@@ -872,8 +872,10 @@ pub const Request = struct {
872872 // Any response to a HEAD request and any response with a 1xx (Informational), 204 (No Content), or 304 (Not Modified)
873873 // status code is always terminated by the first empty line after the header fields, regardless of the header fields
874874 // present in the message
875 if (req.method == .HEAD or req.response.status.class() == .informational or req.response.status == .no_content or req.response.status == .not_modified) {
876 req.response.parser.done = true;
875 if (req.method == .HEAD or req.response.status.class() == .informational or
876 req.response.status == .no_content or req.response.status == .not_modified)
877 {
878 req.response.parser.state = .complete;
877879
878880 return; // the response is empty, no further setup or redirection is necessary
879881 }
......@@ -889,7 +891,7 @@ pub const Request = struct {
889891 } else if (req.response.content_length) |cl| {
890892 req.response.parser.next_chunk_length = cl;
891893
892 if (cl == 0) req.response.parser.done = true;
894 if (cl == 0) req.response.parser.state = .complete;
893895 } else {
894896 // read until the connection is closed
895897 req.response.parser.next_chunk_length = std.math.maxInt(u64);
......@@ -947,7 +949,7 @@ pub const Request = struct {
947949 try req.send(.{});
948950 } else {
949951 req.response.skip = false;
950 if (!req.response.parser.done) {
952 if (req.response.parser.state != .complete) {
951953 switch (req.response.transfer_compression) {
952954 .identity => req.response.compression = .none,
953955 .compress, .@"x-compress" => return error.CompressionNotSupported,
lib/std/http/Server.zig+6-6
......@@ -395,7 +395,7 @@ pub const Response = struct {
395395 return .reset;
396396 }
397397
398 if (!res.request.parser.done) {
398 if (res.request.parser.state != .complete) {
399399 // If the response wasn't fully read, then we need to close the connection.
400400 res.connection.closing = true;
401401 return .closing;
......@@ -534,12 +534,12 @@ pub const Response = struct {
534534 }
535535
536536 fn transferRead(res: *Response, buf: []u8) TransferReadError!usize {
537 if (res.request.parser.done) return 0;
537 if (res.request.parser.state == .complete) return 0;
538538
539539 var index: usize = 0;
540540 while (index == 0) {
541541 const amt = try res.request.parser.read(&res.connection, buf[index..], false);
542 if (amt == 0 and res.request.parser.done) break;
542 if (amt == 0 and res.request.parser.state == .complete) break;
543543 index += amt;
544544 }
545545
......@@ -596,12 +596,12 @@ pub const Response = struct {
596596 } else if (res.request.content_length) |cl| {
597597 res.request.parser.next_chunk_length = cl;
598598
599 if (cl == 0) res.request.parser.done = true;
599 if (cl == 0) res.request.parser.state = .complete;
600600 } else {
601 res.request.parser.done = true;
601 res.request.parser.state = .complete;
602602 }
603603
604 if (!res.request.parser.done) {
604 if (res.request.parser.state != .complete) {
605605 switch (res.request.transfer_compression) {
606606 .identity => res.request.compression = .none,
607607 .compress, .@"x-compress" => return error.CompressionNotSupported,
lib/std/http/protocol.zig+58-50
......@@ -14,7 +14,7 @@ pub const State = enum {
1414 seen_r,
1515 seen_rn,
1616 seen_rnr,
17 finished,
17 headers_end,
1818 /// Begin transfer-encoding: chunked parsing states.
1919 chunk_head_size,
2020 chunk_head_ext,
......@@ -22,46 +22,61 @@ pub const State = enum {
2222 chunk_data,
2323 chunk_data_suffix,
2424 chunk_data_suffix_r,
25 /// When the parser has finished parsing a complete message. A message is
26 /// only complete after the entire body has been read and any trailing
27 /// headers have been parsed.
28 complete,
2529
2630 /// Returns true if the parser is in a content state (ie. not waiting for more headers).
2731 pub fn isContent(self: State) bool {
2832 return switch (self) {
29 .invalid, .start, .seen_n, .seen_r, .seen_rn, .seen_rnr => false,
30 .finished, .chunk_head_size, .chunk_head_ext, .chunk_head_r, .chunk_data, .chunk_data_suffix, .chunk_data_suffix_r => true,
33 .invalid,
34 .start,
35 .seen_n,
36 .seen_r,
37 .seen_rn,
38 .seen_rnr,
39 => false,
40
41 .headers_end,
42 .chunk_head_size,
43 .chunk_head_ext,
44 .chunk_head_r,
45 .chunk_data,
46 .chunk_data_suffix,
47 .chunk_data_suffix_r,
48 .complete,
49 => true,
3150 };
3251 }
3352};
3453
3554pub const HeadersParser = struct {
36 state: State = .start,
55 state: State,
3756 /// A fixed buffer of len `max_header_bytes`.
3857 /// Pointers into this buffer are not stable until after a message is complete.
3958 header_bytes_buffer: []u8,
4059 header_bytes_len: u32,
4160 next_chunk_length: u64,
42 /// Whether this parser is done parsing a complete message.
43 /// A message is only done when the entire payload has been read.
44 done: bool,
4561
4662 /// Initializes the parser with a provided buffer `buf`.
4763 pub fn init(buf: []u8) HeadersParser {
4864 return .{
65 .state = .start,
4966 .header_bytes_buffer = buf,
5067 .header_bytes_len = 0,
51 .done = false,
5268 .next_chunk_length = 0,
5369 };
5470 }
5571
5672 /// Reinitialize the parser.
57 /// Asserts the parser is in the "done" state.
73 /// Asserts the parser is in the `complete` state.
5874 pub fn reset(hp: *HeadersParser) void {
59 assert(hp.done);
75 assert(hp.state == .complete);
6076 hp.* = .{
6177 .state = .start,
6278 .header_bytes_buffer = hp.header_bytes_buffer,
6379 .header_bytes_len = 0,
64 .done = false,
6580 .next_chunk_length = 0,
6681 };
6782 }
......@@ -86,7 +101,8 @@ pub const HeadersParser = struct {
86101 while (true) {
87102 switch (r.state) {
88103 .invalid => unreachable,
89 .finished => return index,
104 .complete => unreachable,
105 .headers_end => return index,
90106 .start => switch (len - index) {
91107 0 => return index,
92108 1 => {
......@@ -110,7 +126,7 @@ pub const HeadersParser = struct {
110126
111127 switch (b16) {
112128 int16("\r\n") => r.state = .seen_rn,
113 int16("\n\n") => r.state = .finished,
129 int16("\n\n") => r.state = .headers_end,
114130 else => {},
115131 }
116132
......@@ -129,7 +145,7 @@ pub const HeadersParser = struct {
129145
130146 switch (b16) {
131147 int16("\r\n") => r.state = .seen_rn,
132 int16("\n\n") => r.state = .finished,
148 int16("\n\n") => r.state = .headers_end,
133149 else => {},
134150 }
135151
......@@ -154,7 +170,7 @@ pub const HeadersParser = struct {
154170
155171 switch (b16) {
156172 int16("\r\n") => r.state = .seen_rn,
157 int16("\n\n") => r.state = .finished,
173 int16("\n\n") => r.state = .headers_end,
158174 else => {},
159175 }
160176
......@@ -164,7 +180,7 @@ pub const HeadersParser = struct {
164180 }
165181
166182 switch (b32) {
167 int32("\r\n\r\n") => r.state = .finished,
183 int32("\r\n\r\n") => r.state = .headers_end,
168184 else => {},
169185 }
170186
......@@ -212,7 +228,7 @@ pub const HeadersParser = struct {
212228
213229 switch (b16) {
214230 int16("\r\n") => r.state = .seen_rn,
215 int16("\n\n") => r.state = .finished,
231 int16("\n\n") => r.state = .headers_end,
216232 else => {},
217233 }
218234 },
......@@ -229,7 +245,7 @@ pub const HeadersParser = struct {
229245
230246 switch (b16) {
231247 int16("\r\n") => r.state = .seen_rn,
232 int16("\n\n") => r.state = .finished,
248 int16("\n\n") => r.state = .headers_end,
233249 else => {},
234250 }
235251
......@@ -246,10 +262,10 @@ pub const HeadersParser = struct {
246262 const b16 = intShift(u16, b32);
247263
248264 if (b32 == int32("\r\n\r\n")) {
249 r.state = .finished;
265 r.state = .headers_end;
250266 return index + i + 4;
251267 } else if (b16 == int16("\n\n")) {
252 r.state = .finished;
268 r.state = .headers_end;
253269 return index + i + 2;
254270 }
255271 }
......@@ -266,7 +282,7 @@ pub const HeadersParser = struct {
266282
267283 switch (b16) {
268284 int16("\r\n") => r.state = .seen_rn,
269 int16("\n\n") => r.state = .finished,
285 int16("\n\n") => r.state = .headers_end,
270286 else => {},
271287 }
272288
......@@ -286,7 +302,7 @@ pub const HeadersParser = struct {
286302 0 => return index,
287303 else => {
288304 switch (bytes[index]) {
289 '\n' => r.state = .finished,
305 '\n' => r.state = .headers_end,
290306 else => r.state = .start,
291307 }
292308
......@@ -318,7 +334,7 @@ pub const HeadersParser = struct {
318334 switch (b16) {
319335 int16("\r\n") => r.state = .seen_rn,
320336 int16("\n\r") => r.state = .seen_rnr,
321 int16("\n\n") => r.state = .finished,
337 int16("\n\n") => r.state = .headers_end,
322338 else => {},
323339 }
324340
......@@ -337,12 +353,12 @@ pub const HeadersParser = struct {
337353
338354 switch (b16) {
339355 int16("\r\n") => r.state = .seen_rn,
340 int16("\n\n") => r.state = .finished,
356 int16("\n\n") => r.state = .headers_end,
341357 else => {},
342358 }
343359
344360 switch (b24) {
345 int24("\n\r\n") => r.state = .finished,
361 int24("\n\r\n") => r.state = .headers_end,
346362 else => {},
347363 }
348364
......@@ -372,8 +388,8 @@ pub const HeadersParser = struct {
372388 }
373389
374390 switch (b16) {
375 int16("\r\n") => r.state = .finished,
376 int16("\n\n") => r.state = .finished,
391 int16("\r\n") => r.state = .headers_end,
392 int16("\n\n") => r.state = .headers_end,
377393 else => {},
378394 }
379395
......@@ -385,7 +401,7 @@ pub const HeadersParser = struct {
385401 0 => return index,
386402 else => {
387403 switch (bytes[index]) {
388 '\n' => r.state = .finished,
404 '\n' => r.state = .headers_end,
389405 else => r.state = .start,
390406 }
391407
......@@ -486,13 +502,6 @@ pub const HeadersParser = struct {
486502 return len;
487503 }
488504
489 /// Returns whether or not the parser has finished parsing a complete
490 /// message. A message is only complete after the entire body has been read
491 /// and any trailing headers have been parsed.
492 pub fn isComplete(r: *HeadersParser) bool {
493 return r.done and r.state == .finished;
494 }
495
496505 pub const CheckCompleteHeadError = error{HttpHeadersOversize};
497506
498507 /// Pushes `in` into the parser. Returns the number of bytes consumed by
......@@ -523,13 +532,12 @@ pub const HeadersParser = struct {
523532 /// See `std.http.Client.Connection for an example of `conn`.
524533 pub fn read(r: *HeadersParser, conn: anytype, buffer: []u8, skip: bool) !usize {
525534 assert(r.state.isContent());
526 if (r.done) return 0;
527
528535 var out_index: usize = 0;
529536 while (true) {
530537 switch (r.state) {
538 .complete => return out_index,
531539 .invalid, .start, .seen_n, .seen_r, .seen_rn, .seen_rnr => unreachable,
532 .finished => {
540 .headers_end => {
533541 const data_avail = r.next_chunk_length;
534542
535543 if (skip) {
......@@ -539,7 +547,8 @@ pub const HeadersParser = struct {
539547 conn.drop(@intCast(nread));
540548 r.next_chunk_length -= nread;
541549
542 if (r.next_chunk_length == 0 or nread == 0) r.done = true;
550 if (r.next_chunk_length == 0 or nread == 0)
551 r.state = .complete;
543552
544553 return out_index;
545554 } else if (out_index < buffer.len) {
......@@ -549,7 +558,8 @@ pub const HeadersParser = struct {
549558 const nread = try conn.read(buffer[0..can_read]);
550559 r.next_chunk_length -= nread;
551560
552 if (r.next_chunk_length == 0 or nread == 0) r.done = true;
561 if (r.next_chunk_length == 0 or nread == 0)
562 r.state = .complete;
553563
554564 return nread;
555565 } else {
......@@ -566,14 +576,12 @@ pub const HeadersParser = struct {
566576 .invalid => return error.HttpChunkInvalid,
567577 .chunk_data => if (r.next_chunk_length == 0) {
568578 if (std.mem.eql(u8, conn.peek(), "\r\n")) {
569 r.state = .finished;
570 r.done = true;
579 r.state = .complete;
571580 } else {
572 // The trailer section is formatted identically to the header section.
581 // The trailer section is formatted identically
582 // to the header section.
573583 r.state = .seen_rn;
574584 }
575 r.done = true;
576
577585 return out_index;
578586 },
579587 else => return out_index,
......@@ -611,21 +619,21 @@ pub const HeadersParser = struct {
611619};
612620
613621inline fn int16(array: *const [2]u8) u16 {
614 return @as(u16, @bitCast(array.*));
622 return @bitCast(array.*);
615623}
616624
617625inline fn int24(array: *const [3]u8) u24 {
618 return @as(u24, @bitCast(array.*));
626 return @bitCast(array.*);
619627}
620628
621629inline fn int32(array: *const [4]u8) u32 {
622 return @as(u32, @bitCast(array.*));
630 return @bitCast(array.*);
623631}
624632
625633inline fn intShift(comptime T: type, x: anytype) T {
626634 switch (@import("builtin").cpu.arch.endian()) {
627 .little => return @as(T, @truncate(x >> (@bitSizeOf(@TypeOf(x)) - @bitSizeOf(T)))),
628 .big => return @as(T, @truncate(x)),
635 .little => return @truncate(x >> (@bitSizeOf(@TypeOf(x)) - @bitSizeOf(T))),
636 .big => return @truncate(x),
629637 }
630638}
631639
test/standalone/http.zig+76-19
......@@ -165,10 +165,11 @@ fn handleRequest(res: *Server.Response) !void {
165165var handle_new_requests = true;
166166
167167fn runServer(srv: *Server) !void {
168 var client_header_buffer: [1024]u8 = undefined;
168169 outer: while (handle_new_requests) {
169170 var res = try srv.accept(.{
170171 .allocator = salloc,
171 .header_strategy = .{ .dynamic = max_header_size },
172 .client_header_buffer = &client_header_buffer,
172173 });
173174 defer res.deinit();
174175
......@@ -244,7 +245,10 @@ pub fn main() !void {
244245 const uri = try std.Uri.parse(location);
245246
246247 log.info("{s}", .{location});
247 var req = try client.open(.GET, uri, h, .{});
248 var server_header_buffer: [1024]u8 = undefined;
249 var req = try client.open(.GET, uri, h, .{
250 .server_header_buffer = &server_header_buffer,
251 });
248252 defer req.deinit();
249253
250254 try req.send(.{});
......@@ -269,7 +273,10 @@ pub fn main() !void {
269273 const uri = try std.Uri.parse(location);
270274
271275 log.info("{s}", .{location});
272 var req = try client.open(.GET, uri, h, .{});
276 var server_header_buffer: [1024]u8 = undefined;
277 var req = try client.open(.GET, uri, h, .{
278 .server_header_buffer = &server_header_buffer,
279 });
273280 defer req.deinit();
274281
275282 try req.send(.{});
......@@ -293,7 +300,10 @@ pub fn main() !void {
293300 const uri = try std.Uri.parse(location);
294301
295302 log.info("{s}", .{location});
296 var req = try client.open(.HEAD, uri, h, .{});
303 var server_header_buffer: [1024]u8 = undefined;
304 var req = try client.open(.HEAD, uri, h, .{
305 .server_header_buffer = &server_header_buffer,
306 });
297307 defer req.deinit();
298308
299309 try req.send(.{});
......@@ -319,7 +329,10 @@ pub fn main() !void {
319329 const uri = try std.Uri.parse(location);
320330
321331 log.info("{s}", .{location});
322 var req = try client.open(.GET, uri, h, .{});
332 var server_header_buffer: [1024]u8 = undefined;
333 var req = try client.open(.GET, uri, h, .{
334 .server_header_buffer = &server_header_buffer,
335 });
323336 defer req.deinit();
324337
325338 try req.send(.{});
......@@ -344,7 +357,10 @@ pub fn main() !void {
344357 const uri = try std.Uri.parse(location);
345358
346359 log.info("{s}", .{location});
347 var req = try client.open(.HEAD, uri, h, .{});
360 var server_header_buffer: [1024]u8 = undefined;
361 var req = try client.open(.HEAD, uri, h, .{
362 .server_header_buffer = &server_header_buffer,
363 });
348364 defer req.deinit();
349365
350366 try req.send(.{});
......@@ -370,7 +386,10 @@ pub fn main() !void {
370386 const uri = try std.Uri.parse(location);
371387
372388 log.info("{s}", .{location});
373 var req = try client.open(.GET, uri, h, .{});
389 var server_header_buffer: [1024]u8 = undefined;
390 var req = try client.open(.GET, uri, h, .{
391 .server_header_buffer = &server_header_buffer,
392 });
374393 defer req.deinit();
375394
376395 try req.send(.{});
......@@ -397,7 +416,10 @@ pub fn main() !void {
397416 const uri = try std.Uri.parse(location);
398417
399418 log.info("{s}", .{location});
400 var req = try client.open(.POST, uri, h, .{});
419 var server_header_buffer: [1024]u8 = undefined;
420 var req = try client.open(.POST, uri, h, .{
421 .server_header_buffer = &server_header_buffer,
422 });
401423 defer req.deinit();
402424
403425 req.transfer_encoding = .{ .content_length = 14 };
......@@ -429,7 +451,10 @@ pub fn main() !void {
429451 const uri = try std.Uri.parse(location);
430452
431453 log.info("{s}", .{location});
432 var req = try client.open(.GET, uri, h, .{});
454 var server_header_buffer: [1024]u8 = undefined;
455 var req = try client.open(.GET, uri, h, .{
456 .server_header_buffer = &server_header_buffer,
457 });
433458 defer req.deinit();
434459
435460 try req.send(.{});
......@@ -456,7 +481,10 @@ pub fn main() !void {
456481 const uri = try std.Uri.parse(location);
457482
458483 log.info("{s}", .{location});
459 var req = try client.open(.POST, uri, h, .{});
484 var server_header_buffer: [1024]u8 = undefined;
485 var req = try client.open(.POST, uri, h, .{
486 .server_header_buffer = &server_header_buffer,
487 });
460488 defer req.deinit();
461489
462490 req.transfer_encoding = .chunked;
......@@ -486,7 +514,10 @@ pub fn main() !void {
486514 const uri = try std.Uri.parse(location);
487515
488516 log.info("{s}", .{location});
489 var req = try client.open(.GET, uri, h, .{});
517 var server_header_buffer: [1024]u8 = undefined;
518 var req = try client.open(.GET, uri, h, .{
519 .server_header_buffer = &server_header_buffer,
520 });
490521 defer req.deinit();
491522
492523 try req.send(.{});
......@@ -510,7 +541,10 @@ pub fn main() !void {
510541 const uri = try std.Uri.parse(location);
511542
512543 log.info("{s}", .{location});
513 var req = try client.open(.GET, uri, h, .{});
544 var server_header_buffer: [1024]u8 = undefined;
545 var req = try client.open(.GET, uri, h, .{
546 .server_header_buffer = &server_header_buffer,
547 });
514548 defer req.deinit();
515549
516550 try req.send(.{});
......@@ -534,7 +568,10 @@ pub fn main() !void {
534568 const uri = try std.Uri.parse(location);
535569
536570 log.info("{s}", .{location});
537 var req = try client.open(.GET, uri, h, .{});
571 var server_header_buffer: [1024]u8 = undefined;
572 var req = try client.open(.GET, uri, h, .{
573 .server_header_buffer = &server_header_buffer,
574 });
538575 defer req.deinit();
539576
540577 try req.send(.{});
......@@ -558,7 +595,10 @@ pub fn main() !void {
558595 const uri = try std.Uri.parse(location);
559596
560597 log.info("{s}", .{location});
561 var req = try client.open(.GET, uri, h, .{});
598 var server_header_buffer: [1024]u8 = undefined;
599 var req = try client.open(.GET, uri, h, .{
600 .server_header_buffer = &server_header_buffer,
601 });
562602 defer req.deinit();
563603
564604 try req.send(.{});
......@@ -580,7 +620,10 @@ pub fn main() !void {
580620 const uri = try std.Uri.parse(location);
581621
582622 log.info("{s}", .{location});
583 var req = try client.open(.GET, uri, h, .{});
623 var server_header_buffer: [1024]u8 = undefined;
624 var req = try client.open(.GET, uri, h, .{
625 .server_header_buffer = &server_header_buffer,
626 });
584627 defer req.deinit();
585628
586629 try req.send(.{});
......@@ -628,7 +671,10 @@ pub fn main() !void {
628671 const uri = try std.Uri.parse(location);
629672
630673 log.info("{s}", .{location});
631 var req = try client.open(.POST, uri, h, .{});
674 var server_header_buffer: [1024]u8 = undefined;
675 var req = try client.open(.POST, uri, h, .{
676 .server_header_buffer = &server_header_buffer,
677 });
632678 defer req.deinit();
633679
634680 req.transfer_encoding = .chunked;
......@@ -659,7 +705,10 @@ pub fn main() !void {
659705 const uri = try std.Uri.parse(location);
660706
661707 log.info("{s}", .{location});
662 var req = try client.open(.POST, uri, h, .{});
708 var server_header_buffer: [1024]u8 = undefined;
709 var req = try client.open(.POST, uri, h, .{
710 .server_header_buffer = &server_header_buffer,
711 });
663712 defer req.deinit();
664713
665714 req.transfer_encoding = .chunked;
......@@ -678,9 +727,17 @@ pub fn main() !void {
678727 var requests = try calloc.alloc(http.Client.Request, total_connections);
679728 defer calloc.free(requests);
680729
730 var header_bufs = std.ArrayList([]u8).init(calloc);
731 defer header_bufs.deinit();
732 defer for (header_bufs.items) |item| calloc.free(item);
733
681734 for (0..total_connections) |i| {
682 var req = try client.open(.GET, uri, .{ .allocator = calloc }, .{});
683 req.response.parser.done = true;
735 const headers_buf = try calloc.alloc(u8, 1024);
736 try header_bufs.append(headers_buf);
737 var req = try client.open(.GET, uri, .{ .allocator = calloc }, .{
738 .server_header_buffer = headers_buf,
739 });
740 req.response.parser.state = .complete;
684741 req.connection.?.closing = false;
685742 requests[i] = req;
686743 }