@@ -6,11 +6,13 @@ const Config = @import("Config.zig");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const Db = @import("Db.zig");
const StaticHttpFileServer = @import("StaticHttpFileServer");
const usage =
\\Usage: groovebasin [options]
\\Options:
\\ --config [file] Defaults to config.zon in the cwd
\\ --install [path] Installation directory. Defaults to the cwd
\\ --config [file] Defaults to config.zon in the install directory
\\ -h, --help Print this help menu to stdout
\\
;
@@ -39,7 +41,12 @@ pub fn main() anyerror!noreturn {
std.log.info("Output device: {s}", .{device.name});
var config_zon_path: []const u8 = "config.zon";
var opt_config_zon_path: ?[]const u8 = null;
var install_directory: std.Build.Cache.Directory = .{
.handle = std.fs.cwd(),
.path = null,
};
{
var i: usize = 1;
while (i < args.len) : (i += 1) {
@@ -47,7 +54,14 @@ pub fn main() anyerror!noreturn {
if (std.mem.eql(u8, arg, "--config")) {
if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
i += 1;
config_zon_path = args[i];
opt_config_zon_path = args[i];
} else if (std.mem.eql(u8, arg, "--install")) {
if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
i += 1;
install_directory = .{
.handle = try std.fs.cwd().openDir(args[i], .{}),
.path = args[i],
};
} else if (std.mem.eql(u8, arg, "-h") or std.mem.eql(u8, arg, "--help")) {
try std.io.getStdOut().writeAll(usage);
std.process.exit(0);
@@ -57,7 +71,14 @@ pub fn main() anyerror!noreturn {
}
}
const config = try Config.load(arena, config_zon_path);
const config = if (opt_config_zon_path) |config_zon_path|
try Config.load(arena, std.fs.cwd(), config_zon_path)
else
try Config.load(arena, install_directory.handle, "config.zon");
const addr = std.net.Address.parseIp(config.host, config.port) catch |err| {
fatal("unable to parse {s}:{d}: {s}", .{ config.host, config.port, @errorName(err) });
};
var thread_pool: std.Thread.Pool = undefined;
try thread_pool.init(.{ .allocator = gpa });
@@ -89,9 +110,75 @@ pub fn main() anyerror!noreturn {
}
std.log.debug("loaded {d} files", .{db.files.items.len});
@panic("TODO");
var static_http_file_server = s: {
const sub_path = "www";
var dir = install_directory.handle.openDir(sub_path, .{ .iterate = true }) catch |err| {
fatal("unable to open static asset directory '{}{s}': {s}", .{
install_directory, sub_path, @errorName(err),
});
};
defer dir.close();
break :s StaticHttpFileServer.init(.{
.allocator = gpa,
.root_dir = dir,
}) catch |err| fatal("unable to init static asset server: {s}", .{@errorName(err)});
};
defer static_http_file_server.deinit(gpa);
var net_server = try addr.listen(.{
.reuse_address = true,
});
defer net_server.deinit();
std.log.info("listening at {}", .{net_server.listen_address});
var server: Server = .{
.net_server = &net_server,
.static_http_file_server = &static_http_file_server,
.db = &db,
.thread_pool = &thread_pool,
};
server.serve();
}
pub const Server = struct {
net_server: *std.net.Server,
static_http_file_server: *StaticHttpFileServer,
db: *Db,
thread_pool: *std.Thread.Pool,
pub fn serve(s: *Server) noreturn {
while (true) {
const connection = s.net_server.accept() catch |err| {
fatal("failed to accept new connection: {s}", .{@errorName(err)});
};
_ = std.Thread.spawn(.{}, connectionThreadRun, .{ s, connection }) catch |err| {
std.log.err("failed to spawn connection handler thread: {s}", .{@errorName(err)});
connection.stream.close();
continue;
};
}
}
fn connectionThreadRun(s: *Server, connection: std.net.Server.Connection) void {
defer connection.stream.close();
handleConnection(s, connection) catch |err| {
std.log.err("connection failed: {s}", .{@errorName(err)});
};
}
fn handleConnection(s: *Server, connection: std.net.Server.Connection) !void {
var read_buffer: [0x4000]u8 = undefined;
var http_server = std.http.Server.init(connection, &read_buffer);
while (http_server.state == .ready) {
var request = try http_server.receiveHead();
try s.static_http_file_server.serve(&request);
}
}
};
fn scanDir(db: *Db, gpa: Allocator, db_dir: Db.Path.Index, it: *std.fs.Dir.Iterator) anyerror!void {
while (try it.next()) |entry| switch (entry.kind) {
.directory => {