srctree

Andrew Kelley parent 9b4fd3f0 74c070ec
serving static http files

README.md added: 149, removed: 15, total 134
@@ -48,6 +48,8 @@ zig build
* example that transcodes a list of input files
* update playlist example to support scripted inputs including pausing,
seeking, volume adjustment
* Serving both https and http on the same port
* Automatic SSL Certificate obtaining and renewal
* youtube-dl example
- automated ytdl nightly fetching. keep it cached, check for release on fetch
error. https://github.com/yt-dlp/yt-dlp-nightly-builds/releases
 
build.zig added: 149, removed: 15, total 134
@@ -68,6 +68,10 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
});
groovebasin.root_module.addImport("player", player);
groovebasin.root_module.addImport("StaticHttpFileServer", b.dependency("StaticHttpFileServer", .{
.optimize = optimize,
.target = target,
}).module("StaticHttpFileServer"));
installBin(b, groovebasin);
 
b.getInstallStep().dependOn(&b.addInstallArtifact(python_dep.artifact("cpython"), .{
@@ -79,6 +83,12 @@ pub fn build(b: *std.Build) void {
.install_dir = .prefix,
.install_subdir = "python/Lib",
});
 
b.installDirectory(.{
.source_dir = b.path("www"),
.install_dir = .prefix,
.install_subdir = "www",
});
}
 
fn installBin(b: *std.Build, bin: *std.Build.Step.Compile) void {
 
build.zig.zon added: 149, removed: 15, total 134
@@ -14,6 +14,10 @@
.url = "https://github.com/allyourcodebase/cpython/archive/1f6318617525af742910c87f5c25c6e846a2c851.tar.gz",
.hash = "122073ab1c22cea7bccd32597b6f02395e740f9c54b829b1b381e81192a8702a037c",
},
.StaticHttpFileServer = .{
.url = "https://github.com/andrewrk/StaticHttpFileServer/archive/b549dc152dd1fd60b6c899614da5561556893a48.tar.gz",
.hash = "122055974386ccbf41491435e66001576176ed4a27c8a9634d14a4e82656d7f24f1b",
},
},
.paths = .{
"README.md",
 
server/Config.zig added: 149, removed: 15, total 134
@@ -11,8 +11,8 @@ music_directory: std.Build.Cache.Directory,
 
const max_bytes = 2 * 1024 * 1024;
 
pub fn load(arena: Allocator, config_zon_path: []const u8) !Config {
const config_bytes = std.fs.cwd().readFileAllocOptions(
pub fn load(arena: Allocator, parent_dir: std.fs.Dir, config_zon_path: []const u8) !Config {
const config_bytes = parent_dir.readFileAllocOptions(
arena,
config_zon_path,
max_bytes,
 
server/main.zig added: 149, removed: 15, total 134
@@ -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 => {
 
filename was Deleted added: 149, removed: 15, total 134
@@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<title>404 Not Found - Groove Basin</title>
</head>
<body>
<h1>404 Not Found</h1>
<p><a href="/">Home</a></p>
</body>
</html>
 
 
filename was Deleted added: 149, removed: 15, total 134
@@ -0,0 +1,17 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Groove Basin</title>
<style>
body {
background-color: black;
color: white;
}
</style>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
 
 
filename was Deleted added: 149, removed: 15, total 134
@@ -0,0 +1,3 @@
(function(){
 
})();