authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-16 17:12:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-23 02:37:11-07:00
logd574875f00db32c40019c69465d450a6a58da67f
treec19f6263bd774f08a28d17666a41495333eb4f89
parent3d61890d2405dc861db1e9e5a334ea6f2415a8f1

Revert "std.http: remove 'done' flag"

This reverts commit 42be972a72c86b32ad8403d082ab42763c6facec. Using a bit to distinguish between headers and trailers is fine. It was just named and documented poorly.

4 files changed, 71 insertions(+), 76 deletions(-)

lib/std/http/Client.zig+9-9
......@@ -649,7 +649,7 @@ pub const Request = struct {
649649 /// Frees all resources associated with the request.
650650 pub fn deinit(req: *Request) void {
651651 if (req.connection) |connection| {
652 if (req.response.parser.state != .complete) {
652 if (!req.response.parser.done) {
653653 // If the response wasn't fully read, then we need to close the connection.
654654 connection.closing = true;
655655 }
......@@ -664,7 +664,7 @@ pub const Request = struct {
664664 // or keep those which will be used.
665665 // This needs to be kept in sync with deinit and request.
666666 fn redirect(req: *Request, uri: Uri) !void {
667 assert(req.response.parser.state == .complete);
667 assert(req.response.parser.done);
668668
669669 req.client.connection_pool.release(req.client.allocator, req.connection.?);
670670 req.connection = null;
......@@ -823,12 +823,12 @@ pub const Request = struct {
823823 }
824824
825825 fn transferRead(req: *Request, buf: []u8) TransferReadError!usize {
826 if (req.response.parser.state == .complete) return 0;
826 if (req.response.parser.done) return 0;
827827
828828 var index: usize = 0;
829829 while (index == 0) {
830830 const amt = try req.response.parser.read(req.connection.?, buf[index..], req.response.skip);
831 if (amt == 0 and req.response.parser.state == .complete) break;
831 if (amt == 0 and req.response.parser.done) break;
832832 index += amt;
833833 }
834834
......@@ -873,7 +873,7 @@ pub const Request = struct {
873873 if (req.response.status == .@"continue") {
874874 // We're done parsing the continue response; reset to prepare
875875 // for the real response.
876 req.response.parser.state = .complete;
876 req.response.parser.done = true;
877877 req.response.parser.reset();
878878
879879 if (req.handle_continue)
......@@ -885,7 +885,7 @@ pub const Request = struct {
885885 // we're switching protocols, so this connection is no longer doing http
886886 if (req.method == .CONNECT and req.response.status.class() == .success) {
887887 connection.closing = false;
888 req.response.parser.state = .complete;
888 req.response.parser.done = true;
889889 return; // the connection is not HTTP past this point
890890 }
891891
......@@ -899,7 +899,7 @@ pub const Request = struct {
899899 if (req.method == .HEAD or req.response.status.class() == .informational or
900900 req.response.status == .no_content or req.response.status == .not_modified)
901901 {
902 req.response.parser.state = .complete;
902 req.response.parser.done = true;
903903 return; // The response is empty; no further setup or redirection is necessary.
904904 }
905905
......@@ -914,7 +914,7 @@ pub const Request = struct {
914914 } else if (req.response.content_length) |cl| {
915915 req.response.parser.next_chunk_length = cl;
916916
917 if (cl == 0) req.response.parser.state = .complete;
917 if (cl == 0) req.response.parser.done = true;
918918 } else {
919919 // read until the connection is closed
920920 req.response.parser.next_chunk_length = std.math.maxInt(u64);
......@@ -973,7 +973,7 @@ pub const Request = struct {
973973 try req.send(.{});
974974 } else {
975975 req.response.skip = false;
976 if (req.response.parser.state != .complete) {
976 if (!req.response.parser.done) {
977977 switch (req.response.transfer_compression) {
978978 .identity => req.response.compression = .none,
979979 .compress, .@"x-compress" => return error.CompressionUnsupported,
lib/std/http/Server.zig+6-6
......@@ -352,7 +352,7 @@ pub const Response = struct {
352352 return .reset;
353353 }
354354
355 if (res.request.parser.state != .complete) {
355 if (!res.request.parser.done) {
356356 // If the response wasn't fully read, then we need to close the connection.
357357 res.connection.closing = true;
358358 return .closing;
......@@ -447,12 +447,12 @@ pub const Response = struct {
447447 }
448448
449449 fn transferRead(res: *Response, buf: []u8) TransferReadError!usize {
450 if (res.request.parser.state == .complete) return 0;
450 if (res.request.parser.done) return 0;
451451
452452 var index: usize = 0;
453453 while (index == 0) {
454454 const amt = try res.request.parser.read(&res.connection, buf[index..], false);
455 if (amt == 0 and res.request.parser.state == .complete) break;
455 if (amt == 0 and res.request.parser.done) break;
456456 index += amt;
457457 }
458458
......@@ -502,9 +502,9 @@ pub const Response = struct {
502502 if (res.request.content_length) |len| {
503503 res.request.parser.next_chunk_length = len;
504504
505 if (len == 0) res.request.parser.state = .complete;
505 if (len == 0) res.request.parser.done = true;
506506 } else {
507 res.request.parser.state = .complete;
507 res.request.parser.done = true;
508508 }
509509 },
510510 .chunked => {
......@@ -513,7 +513,7 @@ pub const Response = struct {
513513 },
514514 }
515515
516 if (res.request.parser.state != .complete) {
516 if (!res.request.parser.done) {
517517 switch (res.request.transfer_compression) {
518518 .identity => res.request.compression = .none,
519519 .compress, .@"x-compress" => return error.CompressionUnsupported,
lib/std/http/protocol.zig+55-60
......@@ -7,76 +7,64 @@ const assert = std.debug.assert;
77const use_vectors = builtin.zig_backend != .stage2_x86_64;
88
99pub const State = enum {
10 /// Begin header parsing states.
1110 invalid,
11
12 // Begin header and trailer parsing states.
13
1214 start,
1315 seen_n,
1416 seen_r,
1517 seen_rn,
1618 seen_rnr,
17 headers_end,
18 /// Begin transfer-encoding: chunked parsing states.
19 finished,
20
21 // Begin transfer-encoding: chunked parsing states.
22
1923 chunk_head_size,
2024 chunk_head_ext,
2125 chunk_head_r,
2226 chunk_data,
2327 chunk_data_suffix,
2428 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,
2929
3030 /// Returns true if the parser is in a content state (ie. not waiting for more headers).
3131 pub fn isContent(self: State) bool {
3232 return switch (self) {
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,
33 .invalid, .start, .seen_n, .seen_r, .seen_rn, .seen_rnr => false,
34 .finished, .chunk_head_size, .chunk_head_ext, .chunk_head_r, .chunk_data, .chunk_data_suffix, .chunk_data_suffix_r => true,
5035 };
5136 }
5237};
5338
5439pub const HeadersParser = struct {
55 state: State,
40 state: State = .start,
5641 /// A fixed buffer of len `max_header_bytes`.
5742 /// Pointers into this buffer are not stable until after a message is complete.
5843 header_bytes_buffer: []u8,
5944 header_bytes_len: u32,
6045 next_chunk_length: u64,
46 /// `false`: headers. `true`: trailers.
47 done: bool,
6148
6249 /// Initializes the parser with a provided buffer `buf`.
6350 pub fn init(buf: []u8) HeadersParser {
6451 return .{
65 .state = .start,
6652 .header_bytes_buffer = buf,
6753 .header_bytes_len = 0,
54 .done = false,
6855 .next_chunk_length = 0,
6956 };
7057 }
7158
7259 /// Reinitialize the parser.
73 /// Asserts the parser is in the `complete` state.
60 /// Asserts the parser is in the "done" state.
7461 pub fn reset(hp: *HeadersParser) void {
75 assert(hp.state == .complete);
62 assert(hp.done);
7663 hp.* = .{
7764 .state = .start,
7865 .header_bytes_buffer = hp.header_bytes_buffer,
7966 .header_bytes_len = 0,
67 .done = false,
8068 .next_chunk_length = 0,
8169 };
8270 }
......@@ -101,8 +89,7 @@ pub const HeadersParser = struct {
10189 while (true) {
10290 switch (r.state) {
10391 .invalid => unreachable,
104 .complete => unreachable,
105 .headers_end => return index,
92 .finished => return index,
10693 .start => switch (len - index) {
10794 0 => return index,
10895 1 => {
......@@ -126,7 +113,7 @@ pub const HeadersParser = struct {
126113
127114 switch (b16) {
128115 int16("\r\n") => r.state = .seen_rn,
129 int16("\n\n") => r.state = .headers_end,
116 int16("\n\n") => r.state = .finished,
130117 else => {},
131118 }
132119
......@@ -145,7 +132,7 @@ pub const HeadersParser = struct {
145132
146133 switch (b16) {
147134 int16("\r\n") => r.state = .seen_rn,
148 int16("\n\n") => r.state = .headers_end,
135 int16("\n\n") => r.state = .finished,
149136 else => {},
150137 }
151138
......@@ -170,7 +157,7 @@ pub const HeadersParser = struct {
170157
171158 switch (b16) {
172159 int16("\r\n") => r.state = .seen_rn,
173 int16("\n\n") => r.state = .headers_end,
160 int16("\n\n") => r.state = .finished,
174161 else => {},
175162 }
176163
......@@ -180,7 +167,7 @@ pub const HeadersParser = struct {
180167 }
181168
182169 switch (b32) {
183 int32("\r\n\r\n") => r.state = .headers_end,
170 int32("\r\n\r\n") => r.state = .finished,
184171 else => {},
185172 }
186173
......@@ -228,7 +215,7 @@ pub const HeadersParser = struct {
228215
229216 switch (b16) {
230217 int16("\r\n") => r.state = .seen_rn,
231 int16("\n\n") => r.state = .headers_end,
218 int16("\n\n") => r.state = .finished,
232219 else => {},
233220 }
234221 },
......@@ -245,7 +232,7 @@ pub const HeadersParser = struct {
245232
246233 switch (b16) {
247234 int16("\r\n") => r.state = .seen_rn,
248 int16("\n\n") => r.state = .headers_end,
235 int16("\n\n") => r.state = .finished,
249236 else => {},
250237 }
251238
......@@ -262,10 +249,10 @@ pub const HeadersParser = struct {
262249 const b16 = intShift(u16, b32);
263250
264251 if (b32 == int32("\r\n\r\n")) {
265 r.state = .headers_end;
252 r.state = .finished;
266253 return index + i + 4;
267254 } else if (b16 == int16("\n\n")) {
268 r.state = .headers_end;
255 r.state = .finished;
269256 return index + i + 2;
270257 }
271258 }
......@@ -282,7 +269,7 @@ pub const HeadersParser = struct {
282269
283270 switch (b16) {
284271 int16("\r\n") => r.state = .seen_rn,
285 int16("\n\n") => r.state = .headers_end,
272 int16("\n\n") => r.state = .finished,
286273 else => {},
287274 }
288275
......@@ -302,7 +289,7 @@ pub const HeadersParser = struct {
302289 0 => return index,
303290 else => {
304291 switch (bytes[index]) {
305 '\n' => r.state = .headers_end,
292 '\n' => r.state = .finished,
306293 else => r.state = .start,
307294 }
308295
......@@ -334,7 +321,7 @@ pub const HeadersParser = struct {
334321 switch (b16) {
335322 int16("\r\n") => r.state = .seen_rn,
336323 int16("\n\r") => r.state = .seen_rnr,
337 int16("\n\n") => r.state = .headers_end,
324 int16("\n\n") => r.state = .finished,
338325 else => {},
339326 }
340327
......@@ -353,12 +340,12 @@ pub const HeadersParser = struct {
353340
354341 switch (b16) {
355342 int16("\r\n") => r.state = .seen_rn,
356 int16("\n\n") => r.state = .headers_end,
343 int16("\n\n") => r.state = .finished,
357344 else => {},
358345 }
359346
360347 switch (b24) {
361 int24("\n\r\n") => r.state = .headers_end,
348 int24("\n\r\n") => r.state = .finished,
362349 else => {},
363350 }
364351
......@@ -388,8 +375,8 @@ pub const HeadersParser = struct {
388375 }
389376
390377 switch (b16) {
391 int16("\r\n") => r.state = .headers_end,
392 int16("\n\n") => r.state = .headers_end,
378 int16("\r\n") => r.state = .finished,
379 int16("\n\n") => r.state = .finished,
393380 else => {},
394381 }
395382
......@@ -401,7 +388,7 @@ pub const HeadersParser = struct {
401388 0 => return index,
402389 else => {
403390 switch (bytes[index]) {
404 '\n' => r.state = .headers_end,
391 '\n' => r.state = .finished,
405392 else => r.state = .start,
406393 }
407394
......@@ -502,6 +489,13 @@ pub const HeadersParser = struct {
502489 return len;
503490 }
504491
492 /// Returns whether or not the parser has finished parsing a complete
493 /// message. A message is only complete after the entire body has been read
494 /// and any trailing headers have been parsed.
495 pub fn isComplete(r: *HeadersParser) bool {
496 return r.done and r.state == .finished;
497 }
498
505499 pub const CheckCompleteHeadError = error{HttpHeadersOversize};
506500
507501 /// Pushes `in` into the parser. Returns the number of bytes consumed by
......@@ -532,12 +526,13 @@ pub const HeadersParser = struct {
532526 /// See `std.http.Client.Connection for an example of `conn`.
533527 pub fn read(r: *HeadersParser, conn: anytype, buffer: []u8, skip: bool) !usize {
534528 assert(r.state.isContent());
529 if (r.done) return 0;
530
535531 var out_index: usize = 0;
536532 while (true) {
537533 switch (r.state) {
538 .complete => return out_index,
539534 .invalid, .start, .seen_n, .seen_r, .seen_rn, .seen_rnr => unreachable,
540 .headers_end => {
535 .finished => {
541536 const data_avail = r.next_chunk_length;
542537
543538 if (skip) {
......@@ -547,8 +542,7 @@ pub const HeadersParser = struct {
547542 conn.drop(@intCast(nread));
548543 r.next_chunk_length -= nread;
549544
550 if (r.next_chunk_length == 0 or nread == 0)
551 r.state = .complete;
545 if (r.next_chunk_length == 0 or nread == 0) r.done = true;
552546
553547 return out_index;
554548 } else if (out_index < buffer.len) {
......@@ -558,8 +552,7 @@ pub const HeadersParser = struct {
558552 const nread = try conn.read(buffer[0..can_read]);
559553 r.next_chunk_length -= nread;
560554
561 if (r.next_chunk_length == 0 or nread == 0)
562 r.state = .complete;
555 if (r.next_chunk_length == 0 or nread == 0) r.done = true;
563556
564557 return nread;
565558 } else {
......@@ -576,12 +569,14 @@ pub const HeadersParser = struct {
576569 .invalid => return error.HttpChunkInvalid,
577570 .chunk_data => if (r.next_chunk_length == 0) {
578571 if (std.mem.eql(u8, conn.peek(), "\r\n")) {
579 r.state = .complete;
572 r.state = .finished;
573 r.done = true;
580574 } else {
581 // The trailer section is formatted identically
582 // to the header section.
575 // The trailer section is formatted identically to the header section.
583576 r.state = .seen_rn;
584577 }
578 r.done = true;
579
585580 return out_index;
586581 },
587582 else => return out_index,
......@@ -619,21 +614,21 @@ pub const HeadersParser = struct {
619614};
620615
621616inline fn int16(array: *const [2]u8) u16 {
622 return @bitCast(array.*);
617 return @as(u16, @bitCast(array.*));
623618}
624619
625620inline fn int24(array: *const [3]u8) u24 {
626 return @bitCast(array.*);
621 return @as(u24, @bitCast(array.*));
627622}
628623
629624inline fn int32(array: *const [4]u8) u32 {
630 return @bitCast(array.*);
625 return @as(u32, @bitCast(array.*));
631626}
632627
633628inline fn intShift(comptime T: type, x: anytype) T {
634629 switch (@import("builtin").cpu.arch.endian()) {
635 .little => return @truncate(x >> (@bitSizeOf(@TypeOf(x)) - @bitSizeOf(T))),
636 .big => return @truncate(x),
630 .little => return @as(T, @truncate(x >> (@bitSizeOf(@TypeOf(x)) - @bitSizeOf(T)))),
631 .big => return @as(T, @truncate(x)),
637632 }
638633}
639634
test/standalone/http.zig+1-1
......@@ -673,7 +673,7 @@ pub fn main() !void {
673673 var req = try client.open(.GET, uri, .{
674674 .server_header_buffer = headers_buf,
675675 });
676 req.response.parser.state = .complete;
676 req.response.parser.done = true;
677677 req.connection.?.closing = false;
678678 requests[i] = req;
679679 }