thread panic: invalid enum value

107 Views Asked by At

I am trying to write a multi threaded reverse proxy in Zig. I started out with simpler ambitions. Writing a multi threaded http server that either echos the request or if the request body is empty returns "Hello, world!"

However I occasionally get this error:

thread 487671 panic: invalid enum value
/home/kaan/code/zig-proxy/src/main.zig:46:36: 0x284b66 in process_request (zig-proxy)
        log.info("{s} {s} {s}", .{ @tagName(response.request.method), @tagName(response.request.version), response.request.target });
                                   ^
/opt/zig-linux-x86_64-0.11.0/lib/std/Thread.zig:433:13: 0x271fd1 in callFn__anon_9094 (zig-proxy)
            @call(.auto, f, args) catch |err| {
            ^
/opt/zig-linux-x86_64-0.11.0/lib/std/Thread.zig:1210:30: 0x241e73 in entryFn (zig-proxy)
                return callFn(f, self.fn_args);
                             ^
/home/kaan/.vscode-server/data/User/globalStorage/ziglang.vscode-zig/zig_install/lib/c.zig:239:13: 0x3288f0 in clone (c)
            asm volatile (
            ^

It doesn't always happen. Sometimes it kind of works. It complains about the response object not being writable but it doesn't crash with the above error. Other times it crashes with a segfault or an integer overflow.

I want to know what I am doing wrong. Here is my code:

const std = @import("std");
const http = std.http;
const log = std.log.scoped(.server);

const server_address = "127.0.0.1";
const server_port = 8888;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer std.debug.assert(gpa.deinit() == .ok);
    const allocator = gpa.allocator();

    var server = http.Server.init(allocator, .{
        .reuse_address = true,
        .reuse_port = true,
    });
    defer server.deinit();

    const address = std.net.Address.parseIp(server_address, server_port) catch unreachable;
    try server.listen(address);

    log.info("listening on {s}:{d}", .{ server_address, server_port });

    while (true) {
        var response = try server.accept(.{
            .allocator = allocator,
        });

        var thread = try std.Thread.spawn(.{}, process_request, .{ &response, allocator });
        thread.detach();
    }
}

fn process_request(response: *http.Server.Response, allocator: std.mem.Allocator) !void {
    defer response.deinit();
    log.info("accepted connection", .{});

    while (response.reset() != .closing) {
        // Handle errors during request processing.
        response.wait() catch |err| switch (err) {
            error.HttpHeadersInvalid => return err,
            error.EndOfStream => continue,
            else => return err,
        };

        log.info("{s} {s} {s}", .{ @tagName(response.request.method), @tagName(response.request.version), response.request.target });

        const body = try response.reader().readAllAlloc(allocator, 8192);
        defer allocator.free(body);

        if (response.request.headers.contains("connection")) {
            try response.headers.append("connection", "keep-alive");
        }

        try response.headers.append("Content-Type", "text/plain");

        try response.do();

        if (response.request.method != .HEAD) {
            try response.writeAll(if (body.len > 0) body else "Hello, World!");
            try response.finish();
        }
    }

    log.info("closing connection", .{});
}
1

There are 1 best solutions below

0
On

For the notWriteable issue, by reading the source code std/http/Client.zig, it seems the problem is because you do not set a Content-Length. Something like:

    const reply = if (body.len > 0) body else "Hello, World!";
    const length = try std.fmt.allocPrint(allocator, "{d}", .{reply.len});
    defer allocator.free(length);
    try response.headers.append("Content-Length", length);

solves this problem.