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", .{});
}
For the
notWriteable
issue, by reading the source codestd/http/Client.zig
, it seems the problem is because you do not set aContent-Length
. Something like:solves this problem.