about summary refs log tree commit diff
path: root/src/main.zig
diff options
context:
space:
mode:
authorRobert Günzler <r@gnzler.io>2026-09-29 00:42:56 +0200
committerRobert Günzler <r@gnzler.io>2026-10-02 10:39:48 +0200
commit5f4ef02e69e0ca2df85144e0824a167642d956d5 (patch)
treec4d9ac34365774f10777727527cfef49c5155c95 /src/main.zig
parentdd2161d0827574c21e5e236ce405a4d0d8969be0 (diff)
downloadsite-5f4ef02e69e0ca2df85144e0824a167642d956d5.tar.gz
make caddy optional
by having an embedded http server

Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to 'src/main.zig')
-rw-r--r--src/main.zig91
1 files changed, 78 insertions, 13 deletions
diff --git a/src/main.zig b/src/main.zig
index fa5b33b..4a87c86 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -123,7 +123,7 @@ const Cli = struct {
             \\  gen [-f, --force] [-l, --list] ROOT-DIR TARGET-DIR
             \\    runs build for relevant files
             \\
-            \\  serve ROOT-DIR
+            \\  serve [-l, --listen PORT] ROOT-DIR
             \\    serves the site (using caddy)
             \\
             \\
@@ -434,24 +434,89 @@ const Cli = struct {
         });
     }
 
-    fn serve(io: Io, _: Allocator, _: FlagSet, args: [][]const u8) !void {
+    fn serve(io: Io, _: Allocator, flags: FlagSet, args: [][]const u8) !void {
         if (args.len != 1) return Cli.usage();
 
         const root_path = args[0];
-        const root_dir = try Io.Dir.cwd().openDir(io, root_path, .{});
-        defer root_dir.close(io);
+        const root_dir = try Dir.init(io, Io.Dir.cwd(), root_path, .{});
+        defer root_dir.fd.close(io);
 
-        const command = blk: {
-            root_dir.access(io, "Caddyfile", .{}) catch |err|
-                switch (err) {
-                    error.FileNotFound => break :blk "caddy file-server -l :1414",
-                    else => return err,
-                };
-            break :blk "caddy fmt --overwrite && caddy run --watch";
+        const progress = if (!DEBUG) std.Progress.start(io, .{ .root_name = "Serving" });
+        defer if (!DEBUG) progress.end();
+
+        // TODO: comptime option?
+        caddy: {
+            root_dir.fd.access(io, "Caddyfile", .{}) catch break :caddy;
+            log.info("Caddy root={s}", .{root_path});
+
+            if (!DEBUG) progress.end();
+            return try shell(
+                io,
+                "caddy fmt --overwrite && caddy run --watch",
+                .{
+                    .cwd = .{ .dir = root_dir.fd },
+                },
+            );
+        }
+
+        const addrStr = flags.get("-l") orelse flags.get("--listen") orelse "127.0.0.1:1414";
+
+        const addr = blk: {
+            const idx = std.mem.lastIndexOfScalar(u8, addrStr, ':') orelse
+                return error.AddrMissingPort;
+
+            const port = try std.fmt.parseInt(u16, addrStr[idx + 1 ..], 0);
+            const host = blk2: {
+                const h = addrStr[0..idx];
+                if (std.mem.eql(u8, h, "")) break :blk2 "127.0.0.1";
+                if (std.mem.eql(u8, h, "localhost")) break :blk2 "127.0.0.1";
+                break :blk2 h;
+            };
+            break :blk try Io.net.IpAddress.parse(host, port);
         };
 
-        log.info("Serving root={s}", .{root_path});
-        try shell(io, command, .{ .cwd = .{ .dir = root_dir } });
+        log.info("Serving root={s} addr={s}", .{ root_path, addrStr });
+
+        const serverNode = if (!DEBUG) progress.start(addrStr, 0);
+        defer if (!DEBUG) progress.end();
+
+        // tcp server
+        var server = try addr.listen(io, .{ .reuse_address = true });
+        defer server.deinit(io);
+
+        while (true) {
+            const stream = try server.accept(io);
+            defer stream.close(io);
+
+            var stream_read_buffer: [BUFFER_SIZE]u8 = undefined;
+            var stream_reader = stream.reader(io, &stream_read_buffer);
+            var stream_write_buffer: [BUFFER_SIZE]u8 = undefined;
+            var stream_writer = stream.writer(io, &stream_write_buffer);
+
+            var http_server = std.http.Server.init(&stream_reader.interface, &stream_writer.interface);
+            var req = try http_server.receiveHead();
+
+            log.info("{s} {s}", .{ @tagName(req.head.method), req.head.target });
+
+            const streamNode = if (!DEBUG) serverNode.start(req.head.target, 0);
+            defer if (!DEBUG) streamNode.end();
+
+            switch (req.head.method) {
+                .GET, .HEAD => {},
+                else => return,
+            }
+
+            var fd = File.init(io, root_dir, req.head.target[1..], null) catch |err| {
+                switch (err) {
+                    error.FileNotFound => try req.respond("", .{ .status = .not_found }),
+                    else => try req.respond("", .{ .status = .internal_server_error }),
+                }
+                continue;
+            };
+            defer fd.close(io);
+
+            try req.respond(fd.mmap.memory, .{ .status = .ok });
+        }
     }
 };