From 9ed20386bbabad2b458fe2f93a0a5a1ed06527cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 2 May 2025 19:46:05 -0700 Subject: [PATCH] std.http.Reader: simplify states --- lib/compiler/std-docs.zig | 2 +- lib/std/http.zig | 35 ++++++++++++++++------------------- lib/std/http/Client.zig | 3 +-- lib/std/http/Server.zig | 6 ++---- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index b9d96b9823..0645120212 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -98,7 +98,7 @@ fn accept(context: *Context, connection: std.net.Server.Connection) void { var connection_bw = stream_writer.interface().buffered(&send_buffer); var server = std.http.Server.init(&connection_br, &connection_bw); - while (server.state == .ready) { + while (server.reader.state == .ready) { var request = server.receiveHead() catch |err| switch (err) { error.HttpConnectionClosing => return, else => { diff --git a/lib/std/http.zig b/lib/std/http.zig index fcb25af130..4ec91d3af5 100644 --- a/lib/std/http.zig +++ b/lib/std/http.zig @@ -334,11 +334,6 @@ pub const Reader = struct { /// Number of bytes of HTTP trailers. These are at the end of a /// transfer-encoding: chunked message. trailers_len: usize = 0, - body_state: union { - none: void, - remaining_content_length: u64, - remaining_chunk_len: RemainingChunkLen, - }, body_err: ?BodyError = null, /// Stolen from `in`. head_buffer: []u8 = &.{}, @@ -361,12 +356,13 @@ pub const Reader = struct { } }; - pub const State = enum { + pub const State = union(enum) { /// The stream is available to be used for the first time, or reused. ready, - receiving_head, received_head, - receiving_body, + body_none: void, + body_remaining_content_length: u64, + body_remaining_chunk_len: RemainingChunkLen, /// The stream would be eligible for another HTTP request, however the /// client and server did not negotiate a persistent connection. closing, @@ -413,6 +409,7 @@ pub const Reader = struct { head_end += hp.feed(buf[head_end..]); if (hp.state == .finished) { reader.head_buffer = in.steal(head_end); + reader.state = .received_head; return; } } @@ -426,10 +423,9 @@ pub const Reader = struct { /// * `interfaceDecompressing` pub fn bodyReader(reader: *Reader, transfer_encoding: TransferEncoding, content_length: ?u64) std.io.Reader { assert(reader.state == .received_head); - reader.state = .receiving_body; return switch (transfer_encoding) { .chunked => { - reader.body_state = .{ .remaining_chunk_len = .head }; + reader.state = .{ .body_remaining_chunk_len = .head }; return .{ .context = reader, .vtable = &.{ @@ -441,7 +437,7 @@ pub const Reader = struct { }, .none => { if (content_length) |len| { - reader.body_state = .{ .remaining_content_length = len }; + reader.state = .{ .body_remaining_content_length = len }; return .{ .context = reader, .vtable = &.{ @@ -451,6 +447,7 @@ pub const Reader = struct { }, }; } else { + reader.state = .body_none; return reader.in.reader(); } }, @@ -473,7 +470,7 @@ pub const Reader = struct { ) std.io.Reader { if (transfer_encoding == .none and content_length == null) { assert(reader.state == .received_head); - reader.state = .receiving_body; + reader.state = .body_none; switch (content_encoding) { .identity => { return reader.in.reader(); @@ -503,7 +500,7 @@ pub const Reader = struct { limit: std.io.Reader.Limit, ) std.io.Reader.RwError!usize { const reader: *Reader = @alignCast(@ptrCast(ctx)); - const remaining_content_length = &reader.body_state.remaining_content_length; + const remaining_content_length = &reader.state.body_remaining_content_length; const remaining = remaining_content_length.*; if (remaining == 0) { reader.state = .ready; @@ -516,7 +513,7 @@ pub const Reader = struct { fn contentLengthReadVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { const reader: *Reader = @alignCast(@ptrCast(context)); - const remaining_content_length = &reader.body_state.remaining_content_length; + const remaining_content_length = &reader.state.body_remaining_content_length; const remaining = remaining_content_length.*; if (remaining == 0) { reader.state = .ready; @@ -529,7 +526,7 @@ pub const Reader = struct { fn contentLengthDiscard(ctx: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize { const reader: *Reader = @alignCast(@ptrCast(ctx)); - const remaining_content_length = &reader.body_state.remaining_content_length; + const remaining_content_length = &reader.state.body_remaining_content_length; const remaining = remaining_content_length.*; if (remaining == 0) { reader.state = .ready; @@ -546,7 +543,7 @@ pub const Reader = struct { limit: std.io.Reader.Limit, ) std.io.Reader.RwError!usize { const reader: *Reader = @alignCast(@ptrCast(ctx)); - const chunk_len_ptr = &reader.body_state.remaining_chunk_len; + const chunk_len_ptr = &reader.state.body_remaining_chunk_len; const in = reader.in; len: switch (chunk_len_ptr.*) { .head => { @@ -594,7 +591,7 @@ pub const Reader = struct { fn chunkedReadVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { const reader: *Reader = @alignCast(@ptrCast(ctx)); - const chunk_len_ptr = &reader.body_state.remaining_chunk_len; + const chunk_len_ptr = &reader.state.body_remaining_chunk_len; const in = reader.in; var already_requested_more = false; var amt_read: usize = 0; @@ -662,7 +659,7 @@ pub const Reader = struct { fn chunkedDiscard(ctx: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize { const reader: *Reader = @alignCast(@ptrCast(ctx)); - const chunk_len_ptr = &reader.body_state.remaining_chunk_len; + const chunk_len_ptr = &reader.state.body_remaining_chunk_len; const in = reader.in; len: switch (chunk_len_ptr.*) { .head => { @@ -719,7 +716,7 @@ pub const Reader = struct { try in.fill(trailers_len + 1); trailers_len += hp.feed(in.bufferContents()[trailers_len..]); if (hp.state == .finished) { - reader.body_state.remaining_chunk_len = .done; + reader.state.body_remaining_chunk_len = .done; reader.state = .ready; reader.trailers_len = trailers_len; return amt_read; diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index dffdcdd82e..dc01e53be9 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -259,7 +259,7 @@ pub const Connection = struct { const host_buffer = base[@sizeOf(Plain)..][0..remote_host.len]; const socket_read_buffer = host_buffer.ptr[host_buffer.len..][0..client.read_buffer_size]; const socket_write_buffer = socket_read_buffer.ptr[socket_read_buffer.len..][0..client.write_buffer_size]; - assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len); + assert(base.ptr + alloc_len == socket_write_buffer.ptr + socket_write_buffer.len); @memcpy(host_buffer, remote_host); const plain: *Plain = @ptrCast(base); plain.* = .{ @@ -1545,7 +1545,6 @@ pub fn request( .reader = .{ .in = &connection.reader, .state = .ready, - .body_state = undefined, }, .keep_alive = options.keep_alive, .method = method, diff --git a/lib/std/http/Server.zig b/lib/std/http/Server.zig index 0ee5846d23..6cf5748271 100644 --- a/lib/std/http/Server.zig +++ b/lib/std/http/Server.zig @@ -25,7 +25,6 @@ pub fn init(in: *std.io.BufferedReader, out: *std.io.BufferedWriter) Server { .reader = .{ .in = in, .state = .ready, - .body_state = undefined, }, .out = out, }; @@ -234,7 +233,6 @@ pub const Request = struct { .reader = .{ .in = &br, .state = .ready, - .body_state = undefined, }, .out = undefined, }; @@ -522,7 +520,7 @@ pub const Request = struct { /// Returns whether the connection should remain persistent. /// - /// If it would fail, it instead sets the Server state to `receiving_body` + /// If it would fail, it instead sets the Server state to receiving body /// and returns false. fn discardBody(request: *Request, keep_alive: bool) bool { // Prepare to receive another request on the same connection. @@ -541,7 +539,7 @@ pub const Request = struct { assert(r.state == .ready); return true; }, - .receiving_body, .ready => return true, + .body_remaining_content_length, .body_remaining_chunk_len, .body_none, .ready => return true, else => unreachable, };