Hi!
I am making a small http server executable as a personal utility, and this is the code (its messy and probably slow):
const std = @import("std");
const index_path = "index.html";
const not_found_path = "404.html";
pub fn main(init: std.process.Init) !void {
var arena_allocator: std.heap.ArenaAllocator = .init(init.gpa);
const arena = arena_allocator.allocator();
var it = try init.minimal.args.iterateAllocator(arena);
_ = it.next();
const ip = it.next() orelse "127.0.0.1";
const port = if (it.next()) |arg|
try std.fmt.parseInt(u16, arg, 10)
else
8080;
const address: std.Io.net.IpAddress = try .parse(ip, port);
arena_allocator.deinit();
var server = try address.listen(init.io, .{});
defer server.deinit(init.io);
std.log.info("Listening on {s}:{d}.", .{ ip, port });
while (true) {
var stream = server.accept(init.io) catch |err| {
std.log.err("Connection failed: {s}.", .{@errorName(err)});
continue;
};
defer stream.close(init.io);
std.log.info("Connection successful.", .{});
var stream_reader_buffer: [1024]u8 = undefined;
var stream_reader = stream.reader(init.io, &stream_reader_buffer);
var stream_writer_buffer: [1024]u8 = undefined;
var stream_writer = stream.writer(init.io, &stream_writer_buffer);
var http_server = std.http.Server.init(&stream_reader.interface, &stream_writer.interface);
var request = http_server.receiveHead() catch |err| {
std.log.err("Request read failed: {s}.", .{@errorName(err)});
continue;
};
std.log.info("Request read successful: {} {s}.", .{ request.head.method, request.head.target });
const target_path = if (std.mem.eql(u8, request.head.target, "/"))
request.head.target
else
index_path;
var respond_options: std.http.Server.Request.RespondOptions = .{
.version = .@"HTTP/1.0",
.status = .ok,
.extra_headers = &.{.{
.name = "Content-Type",
.value = "text/html",
}},
};
var target_file: std.Io.File = std.Io.Dir.cwd().openFile(init.io, target_path, .{ .resolve_beneath = true }) catch |err| blk: {
if (err == error.FileNotFound) {
const not_found_file = std.Io.Dir.cwd().openFile(init.io, not_found_path, .{ .resolve_beneath = true }) catch {
std.log.err("Target open failed: FileNotFound.", .{});
continue;
};
respond_options.status = .not_found;
break :blk not_found_file;
} else {
std.log.err("Target open failed: {s}.", .{@errorName(err)});
continue;
}
};
defer target_file.close(init.io);
var target_reader_buffer: [4096]u8 = undefined;
var target_reader = target_file.reader(init.io, &target_reader_buffer);
const response = target_reader.interface.allocRemaining(init.gpa, .limited(1024 * 1024 * 1024)) catch |err| {
std.log.err("Target read failed: {s}.", .{@errorName(err)});
continue;
};
request.respond(response, respond_options) catch |err| {
std.log.err("Response write failed: {s}.", .{@errorName(err)});
continue;
};
std.log.info("Response write successful.", .{});
}
}
Whenever I run curl 127.0.0.1:8080, the logs show this:
info: Connection successful.
info: Request read successful: .GET /.
unexpected errno: 107
/opt/homebrew/Cellar/zig/0.17.1/lib/zig/std/posix.zig:1678:40: 0x1046630bb in unexpectedErrno (zhs)
std.debug.dumpCurrentStackTrace(.{});
^
/opt/homebrew/Cellar/zig/0.17.1/lib/zig/std/Io/Threaded.zig:5100:67: 0x1046ed153 in dirOpenFilePosix (zhs)
else => |err| return posix.unexpectedErrno(err),
^
/opt/homebrew/Cellar/zig/0.17.1/lib/zig/std/Io/Dir.zig:581:33: 0x104611ca7 in openFile (zhs)
return io.vtable.dirOpenFile(io.userdata, dir, sub_path, options);
^
/Users/vexo/Projects/zhs/src/main.zig:62:65: 0x104718a83 in main (zhs)
var target_file: std.Io.File = std.Io.Dir.cwd().openFile(init.io, target_path, .{ .resolve_beneath = true }) catch |err| blk: {
^
/opt/homebrew/Cellar/zig/0.17.1/lib/zig/std/start.zig:827:30: 0x104719363 in callMain (zhs)
return wrapMain(root.main(.{
^
???:?:?: 0x197bdfe7f in start (/usr/lib/dyld)
error: Target open failed: Unexpected.
I looked up the error number and it means: “Transport endpoint is not connected”, and it is caused when the connection is dropped, but when looking at the stack trace it’s showing the openFile function and the io stuff it does for that. Is file opening not allowed on the same Io instance as a std.Io.net.Server or some other network related thing? What can I do to fix this code?