#std.http.Server deadlock with multiple requests on one connection

1 messages · Page 1 of 1 (latest)

nimble hornet
#

Hey! I'm writing a web backend in Zig using the new std.http.Server (insane, I know). It's currently serving a HTML document with a stylesheet and a script linked. The browser can request the document and the script just fine, but indefinetely hangs on the stylesheet. This is very strange, as the exact same endpoint works just fine without loading the rest of the document. This makes me conclude that the problem here comes from the large amount of requests the browser sends.

This is how my code looks:

pub fn main() !void {
    // create server and make it listen
    while (true) {
        const res = try server.accept(.{ .dynamic = 1024 * 2 });

        (try std.Thread.spawn(.{}, handleRequest, .{ res })).detach();
    }
}

fn handleRequest(res: *std.http.Server.Response) !void {
    defer res.reset();

    try res.wait();
    res.headers.connection = res.request.headers.connection;

    // this is not reached in the request for the stylesheet from firefox!
    std.log.info("req: {s}", .{res.request.headers.target});

    // some logic to decide what to serve here

    res.headers.custom = &.{.{ .name = "Content-Type", "text/css" }};
    res.headers.transfer_encoding = .{ .content_length = data.len };

    try res.do();
    try res.writer().writeAll(data);
    try res.finish();
}

Note: while firefox is waiting for a response (forever), I can request the CSS file with curl just fine.

sick compass
#

have you seen the example in the http server pr?

nimble hornet
#

maybe, not sure which PR you mean

sick compass
nimble hornet
#

...i just keep sending stuff over and over until the connection closes?

sick compass
#

a keep alive connection will have multiple requests

#

res.reset() prepares the response to handle a new request (when keepalive) and cleans up (when not)

#

res.headers.connection = res.request.headers.connection is telling the server that you're going to honor whatever decision it makes, and browsers will try to use keepalive

nimble hornet
#

i see

sick compass
#

also, res.reset() currently leaks the *Response

nimble hornet
#

is it intentional that the example has no finish call?

sick compass
#

no

#

I've already found like 4 issues with it

nimble hornet
#

res.connection.closing also doesnt exist

#

xD

#

it works!!

sick compass
nimble hornet
#

yes im aware. i actually got pretty confused when i updated zig today and my code wasnt broken.

#

but i just ported this while codebase from a hacky implementation with a C server library so ive been through waaaaaay worse

#

oh and also, how do i free the memory allocated by a response?

sick compass
#

res.reset() will free most of it, you'll need to manually res.server.allocator.destroy(res) after the while loop breaks

nimble hornet
#

that just caused a segfault for me though

sick compass
#

I think you're right, iirc I set it to undefined in reset. in that case, you'll leak a *Response for every connection

nimble hornet
#

hows that possible? i call reset multiple times in the loop

#

well i guess if i save alloc to a variable first that should work

sick compass
#

reset does something different if the connection is keepalive vs closing