···1616 // Parse a basic request. This will put our conn.req_state into a valid state
1717 // for creating a request. Application code can modify the request directly
1818 // thereafter to change whatever properties they want.
1919- var base_request = std.io.fixedBufferStream("GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n");
1919+ var base_request: std.Io.Reader = .fixed("GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n");
2020 while (true) {
2121 const done = conn.req_state.parse(conn.req_arena.allocator(), &base_request) catch unreachable;
2222 if (done) {
···261261 }
262262263263 var is_first = true;
264264+ var reader = stream.reader(&.{}); // Request.State does its own buffering
264265 while (true) {
265265- const done = conn.req_state.parse(conn.req_arena.allocator(), stream) catch |err| switch (err) {
266266- error.WouldBlock => {
267267- if (is_keepalive and is_first) {
268268- metrics.timeoutKeepalive(1);
269269- } else {
270270- metrics.timeoutRequest(1);
271271- }
272272- return .close;
273273- },
274274- error.NotOpenForReading => {
275275- // This can only happen when we're shutting down and our
276276- // listener has called posix.close(socket) to unblock
277277- // this thread. Using `.disown` is a bit of a hack, but
278278- // disown is handled in handleConnection the way we want
279279- // WE DO NOT WANT to return .close, else that would result
280280- // in posix.close(socket) being called on an already-closed
281281- // socket, which would panic.
282282- return .disown;
283283- },
284284- else => {
285285- requestError(conn, err) catch {};
286286- posix.close(stream.handle);
287287- return .disown;
288288- },
266266+ const done = conn.req_state.parse(conn.req_arena.allocator(), reader.interface()) catch |err| {
267267+ switch (err) {
268268+ error.ReadFailed => {
269269+ if (reader.getError()) |e| {
270270+ switch (e) {
271271+ error.WouldBlock => {
272272+ if (is_keepalive and is_first) {
273273+ metrics.timeoutKeepalive(1);
274274+ } else {
275275+ metrics.timeoutRequest(1);
276276+ }
277277+ return .close;
278278+ },
279279+ error.NotOpenForReading => {
280280+ // This can only happen when we're shutting down and our
281281+ // listener has called posix.close(socket) to unblock
282282+ // this thread. Using `.disown` is a bit of a hack, but
283283+ // disown is handled in handleConnection the way we want
284284+ // WE DO NOT WANT to return .close, else that would result
285285+ // in posix.close(socket) being called on an already-closed
286286+ // socket, which would panic.
287287+ return .disown;
288288+ },
289289+ else => {},
290290+ }
291291+ }
292292+ },
293293+ else => {},
294294+ }
295295+ requestError(conn, err) catch {};
296296+ posix.close(stream.handle);
297297+ return .disown;
289298 };
290299291300 if (done) {
···583592 // can access _state directly.
584593585594 const stream = http_conn.stream;
586586- const done = http_conn.req_state.parse(http_conn.req_arena.allocator(), stream) catch |err| {
595595+ var reader = stream.reader(&.{}); // Request.State does its own buffering
596596+ const done = http_conn.req_state.parse(http_conn.req_arena.allocator(), reader.interface()) catch |err| {
587597 // maybe a write fail or something, doesn't matter, we're closing the connection
588588- requestError(http_conn, err) catch {};
598598+ requestError(http_conn, reader.getError() orelse err) catch {};
589599590600 // impossible to fail when false is passed
591601 http_conn.requestDone(self.retain_allocated_bytes, false) catch unreachable;
···17751785 metrics.bodyTooBig();
17761786 return writeError(handle, 413, "Request body is too big");
17771787 },
17781778- error.BrokenPipe, error.ConnectionClosed, error.ConnectionResetByPeer => return,
17881788+ error.BrokenPipe, error.ConnectionClosed, error.ConnectionResetByPeer, error.EndOfStream => return,
17791789 else => {
17801790 log.err("server error: {}", .{err});
17811791 metrics.internalError();