commit c9aa5dbae5b08b1f54ae73f2e47b54ca933bce88
parent 11855d91f77cf37062e3cba32bd7c3d63ed304a7
Author: Martin Ashby <martin@ashbysoft.com>
Date:   Sun, 29 Dec 2024 09:51:11 +0000
Remove unused server/ folder
Diffstat:
4 files changed, 0 insertions(+), 521 deletions(-)
diff --git a/server/build.zig b/server/build.zig
@@ -1,36 +0,0 @@
-const std = @import("std");
-
-pub fn build(b: *std.Build) void {
-    const target = b.standardTargetOptions(.{});
-    const optimize = b.standardOptimizeOption(.{});
-
-    const exe = b.addExecutable(.{
-        .name = "server",
-        .root_source_file = .{ .path = "src/main.zig" },
-        .target = target,
-        .optimize = optimize,
-    });
-
-    b.installArtifact(exe);
-    const run_cmd = b.addRunArtifact(exe);
-
-    run_cmd.step.dependOn(b.getInstallStep());
-
-    if (b.args) |args| {
-        run_cmd.addArgs(args);
-    }
-
-    const run_step = b.step("run", "Run the app");
-    run_step.dependOn(&run_cmd.step);
-
-    const exe_unit_tests = b.addTest(.{
-        .root_source_file = .{ .path = "src/main.zig" },
-        .target = target,
-        .optimize = optimize,
-    });
-
-    const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
-
-    const test_step = b.step("test", "Run unit tests");
-    test_step.dependOn(&run_exe_unit_tests.step);
-}
diff --git a/server/build.zig.zon b/server/build.zig.zon
@@ -1,9 +0,0 @@
-.{
-    .name = "server",
-    .version = "0.0.0",
-    .dependencies = .{
-    },
-    .paths = .{
-        "",
-    },
-}
diff --git a/server/src/main.zig b/server/src/main.zig
@@ -1,431 +0,0 @@
-const std = @import("std");
-const log = std.log.scoped(.server);
-
-// Web server for mfashby.net
-// It does _not_ follow the unix philosophy (:
-// Initially it replaces caddy, and it needs to be capable of these things 
-// to actually be usable
-//  - http(s) (otherwise it's not a web server...) ✅ https isn't supported because zig http server (in fact there's no TLS server implementation). For now I'll have to use haproxy
-//  - routing, including virtual host              ✅
-//  - serving static content from selected folders ✅
-//  - executing CGI programs                       ✅
-//  - reverse proxy                                ❌
-
-// And I should probably test it thoroughly before exposing is to the 'net
-
-// Future possibilities: 
-//  - subsume some CGI programs directly into the web server e.g. my comments program
-//  - and maybe even cgit (although I might scrap it in favour of stagit if I can figure out archive downloads
-//  - do something about efficiency :) it's thread-per-request right now
-pub fn main() !void {
-    var gpa = std.heap.GeneralPurposeAllocator(.{ .thread_safe = true }){};
-    defer _ = gpa.deinit();
-    const a = gpa.allocator();
-    var pool: std.Thread.Pool = undefined;
-    try std.Thread.Pool.init(&pool, .{ .allocator = a, .n_jobs = 32 });
-    defer pool.deinit();
-    var svr = std.http.Server.init(.{ .reuse_address = true, .reuse_port = true });
-    defer svr.deinit();
-    const port = 8081;
-    const addr = std.net.Address.initIp4(.{0,0,0,0}, port);
-    try svr.listen(addr);
-    log.info("listening on {}", .{addr});
-    try acceptLoop(a, &svr, &pool, struct{
-        fn cancelled() bool {
-            return false;
-        }
-    });
-}
-
-fn acceptLoop(a: std.mem.Allocator, svr: *std.http.Server, pool: *std.Thread.Pool, cancel: anytype) !void {
-    while (!cancel.cancelled()) {
-        // Create the Response into the heap so that the ownership goes to the handling thread
-        const res = try a.create(std.http.Server.Response);
-        errdefer a.destroy(res);
-        res.* = try svr.accept(.{ .allocator = a });
-        errdefer res.deinit();
-        if (cancel.cancelled()) {
-            res.deinit();
-            a.destroy(res);
-            break;
-        }
-        try pool.spawn(handle, .{res});
-    }
-}
-
-fn handle(res: *std.http.Server.Response) void {
-    defer res.allocator.destroy(res);
-    defer res.deinit();
-    handleRoute(res) catch |e| {
-        log.info("error {}", .{e});
-        if (@errorReturnTrace()) |trace| {
-            std.debug.dumpStackTrace(trace.*);
-        }
-        sendError(res, e) catch |e2| {
-            log.info("error sending error {}", .{e2});
-            if (@errorReturnTrace()) |trace| {
-                std.debug.dumpStackTrace(trace.*);
-            }
-        };
-    };
-}
-
-fn handleRoute(res: *std.http.Server.Response) !void {
-    try res.wait();
-
-    // Route, virtual host first
-    var host: []const u8 = "";
-    if (res.request.headers.getFirstValue("Host")) |host_header| {
-        var spl = std.mem.splitScalar(u8, host_header, ':');
-        host = spl.first();
-    }
-
-    const uri = std.Uri.parseWithoutScheme(res.request.target) catch {
-        res.status = .bad_request;
-        const msg = "bad request target";
-        res.transfer_encoding = .{ .content_length = msg.len };
-        try res.send();
-        try res.writeAll(msg);
-        try res.finish();
-        return;
-    };
-
-    if (std.mem.eql(u8, host, "localhost")) {
-        if (std.mem.startsWith(u8,  uri.path, "/api")) {
-            try serveCgi(res, "/home/martin/dev/mfashby.net/comments/zig-out/bin/comments",
-                &.{"DATABASE_URL","SMTP_USERNAME","SMTP_PASSWORD","NOTIFICATION_ADDRESS","SMTP_SERVER"});
-        } else {
-            try serveStatic(res, "public");
-        }
-    } else {
-        const ans = "You have reached mfashby.net ... but did you mean to?";
-        res.status = .ok;
-        res.transfer_encoding = .{ .content_length = ans.len };
-        try res.send();
-        try res.writeAll(ans);
-        try res.finish();
-    }
-}
-
-fn serveCgi(res: *std.http.Server.Response, executable: []const u8,
-            comptime env_copy: []const []const u8) !void {
-    var child = std.ChildProcess.init(&.{executable}, res.allocator);
-    child.stdin_behavior = .Pipe;
-    child.stdout_behavior = .Pipe;
-    child.stderr_behavior = .Pipe;
-
-    var env = std.process.EnvMap.init(res.allocator);
-    try env.put("REQUEST_METHOD", @tagName(res.request.method));
-    try env.put("REQUEST_URI", res.request.target);
-    inline for (env_copy) |key| {
-        try env.put(key, std.os.getenv(key) orelse "");
-    }
-    var clb = [_]u8{0}**30;
-    if (res.request.method == .POST) {
-        if (res.request.content_length) |cl| {
-            try env.put("CONTENT_LENGTH", try std.fmt.bufPrint(&clb, "{}", .{cl}));
-        }
-    }
-    child.env_map = &env;
-    try child.spawn();
-    
-    if (res.request.method == .POST) {
-        if (res.request.content_length) |cl| {
-            log.info("sending {} data as CGI body", .{cl});
-            try pump(res.reader(), child.stdin.?.writer(), cl);
-        } else {
-            _ = try pumpUnknown(res.reader(), child.stdin.?.writer());
-        }
-    }
-    var stdout = std.ArrayList(u8).init(res.allocator);
-    var stderr = std.ArrayList(u8).init(res.allocator);
-    defer stdout.deinit();
-    defer stderr.deinit();
-    defer {
-        if (stderr.items.len>0) {
-            log.err("CGI error: {s}", .{stderr.items});
-        }
-    }
-    try child.collectOutput(&stdout, &stderr, 1_000_000);
-    const term = try child.wait();
-    if (term.Exited != 0) {
-        return error.ProcessError;
-    }
-
-    var fbs = std.io.fixedBufferStream(stdout.items);
-    var reader = fbs.reader();
-    var headerLine = std.ArrayList(u8).init(res.allocator);
-    defer headerLine.deinit();
-    while (true) {
-        headerLine.clearRetainingCapacity();
-        try reader.streamUntilDelimiter(headerLine.writer(), '\r', 8192);
-        _ = try reader.skipBytes(1, .{}); // \n
-        if (headerLine.items.len == 0) {
-            break;
-        }
-        var spl = std.mem.splitScalar(u8, headerLine.items, ':');
-        const key = try std.ascii.allocLowerString(res.allocator, spl.first());
-        defer res.allocator.free(key);
-        if (std.mem.eql(u8, key, "status")) {
-            const value = spl.rest();
-            var spl2 = std.mem.splitScalar(u8, std.mem.trim(u8, value, " "), ' ');
-            res.status = @enumFromInt(try std.fmt.parseInt(u16, spl2.first(), 10));
-            log.info("status from CGI {}", .{res.status});
-        } else if (std.mem.eql(u8, key, "content-length")) {
-            const value = spl.rest();
-            res.transfer_encoding = .{.content_length = try std.fmt.parseInt(usize, value, 10)};
-            log.info("transfer_encoding from CGI {}", .{res.transfer_encoding});
-        } else {
-            const value = spl.rest();
-            try res.headers.append(key, std.mem.trim(u8, value, " "));
-            log.info("header from CGI {s}: {s}", .{key, value});
-        }
-    }
-
-    if (res.transfer_encoding == .content_length) {
-        try res.send();
-        try pump(reader, res.writer(), res.transfer_encoding.content_length);
-    } else {
-        res.transfer_encoding = .chunked;
-        try res.send();
-        _ = try pumpUnknown(reader, res.writer());
-    }
-    try res.finish();
-}
-
-fn serveStatic(res: *std.http.Server.Response, dirname: []const u8) !void {
-    const dirpath = try std.fs.realpathAlloc(res.allocator, dirname);
-    defer res.allocator.free(dirpath);
-
-    // Path massaging
-    const uri = std.Uri.parseWithoutScheme(res.request.target) catch {
-        res.status = .bad_request;
-        const msg = "bad request target";
-        res.transfer_encoding = .{ .content_length = msg.len };
-        try res.send();
-        try res.writeAll(msg);
-        try res.finish();
-        return;
-    };
-    var requested_path = uri.path;
-    requested_path = try std.fs.path.join(res.allocator, &.{ dirpath, requested_path });
-    defer res.allocator.free(requested_path);
-
-
-    const path = std.fs.realpathAlloc(res.allocator, requested_path) catch |e| {
-        res.status = switch (e) {
-            error.FileNotFound => .not_found,
-            error.AccessDenied => .forbidden,
-            error.BadPathName => .bad_request,
-            else => .internal_server_error,
-        };
-        const msg = try std.fmt.allocPrint(res.allocator, "error: {}", .{e});
-        defer res.allocator.free(msg);
-        res.transfer_encoding = .{ .content_length = msg.len };
-        try res.send();
-        try res.writeAll(msg);
-        try res.finish();
-        return;
-    };
-
-    defer res.allocator.free(path);
-    if (!std.mem.startsWith(u8, path, dirpath)) {
-        res.status = .bad_request;
-        const msg = try std.fmt.allocPrint(res.allocator, "Trying to escape the root directory {s}", .{path});
-        defer res.allocator.free(msg);
-        res.transfer_encoding = .{ .content_length = msg.len };
-        try res.send();
-        try res.writeAll(msg);
-        try res.finish();
-        return;
-    }
-    const f = std.fs.openFileAbsolute(path, .{}) catch |e| {
-        res.status = switch (e) {
-            error.FileNotFound => .not_found,
-            error.AccessDenied => .forbidden,
-            error.BadPathName, error.NameTooLong => .bad_request,
-            else => .internal_server_error,
-        };
-        const msg = try std.fmt.allocPrint(res.allocator, "error: {}", .{e});
-        defer res.allocator.free(msg);
-        res.transfer_encoding = .{ .content_length = msg.len };
-        try res.send();
-        try res.writeAll(msg);
-        try res.finish();
-        return;
-    };
-    defer f.close();
-    const stat = try f.stat();
-    switch (stat.kind) {
-        .file => {
-            res.transfer_encoding = .{ .content_length = stat.size };
-            try res.send();
-            try pump(f.reader(), res.writer(), stat.size);
-            try res.finish();
-        },
-        .directory => {
-            const index_path = try std.fs.path.join(res.allocator, &.{path, "index.html"});
-            defer res.allocator.free(index_path);
-            const index_f = std.fs.openFileAbsolute(index_path, .{}) catch |e| {
-                res.status = switch (e) {
-                    error.FileNotFound => .not_found,
-                    error.AccessDenied => .forbidden,
-                    error.BadPathName, error.NameTooLong => .bad_request,
-                    else => .internal_server_error,
-                };
-                const msg = try std.fmt.allocPrint(res.allocator, "error: {}", .{e});
-                defer res.allocator.free(msg);
-                res.transfer_encoding = .{ .content_length = msg.len };
-                try res.send();
-                try res.writeAll(msg);
-                try res.finish();
-                return;
-            };
-            defer index_f.close();
-            const index_stat = try index_f.stat();
-            res.transfer_encoding = .{ .content_length = index_stat.size };
-            try res.send();
-            try pump(index_f.reader(), res.writer(), index_stat.size);
-            try res.finish();  
-        },
-        else => {
-            const msg = "unable to serve unsupported file kind";
-            res.status = .unavailable_for_legal_reasons;
-            res.transfer_encoding = .{.content_length = msg.len};
-            try res.send();
-            try res.writeAll(msg);
-            try res.finish();
-        }
-    }
-    
-}
-
-fn pumpUnknown(reader: anytype, writer: anytype) !usize {
-    var read: usize = 0;
-    var buf: [1024]u8 = undefined;
-    while (true) {
-        const sz = try reader.read(&buf);
-        if (sz == 0) break;
-        read += sz;
-        try writer.writeAll(buf[0..sz]);
-    }
-    return read;
-}
-
-fn pump(reader: anytype, writer: anytype, expected: usize) !void {
-    var read: usize = 0;
-    var buf: [1024]u8 = undefined;
-    while (true) {
-        const sz = try reader.read(&buf);
-        if (sz == 0) break;
-        read += sz;
-        if (read > expected) return error.TooMuchData;
-        try writer.writeAll(buf[0..sz]);
-    }
-    if (read != expected) {
-        return error.NotEnoughData;
-    }
-}
-
-
-fn sendError(res: *std.http.Server.Response, e: anyerror) !void {
-    switch (res.state) {
-        .first, .start, .waited => {
-            if (res.state != .waited) {
-                try res.wait();
-            }
-            const errmsg = try std.fmt.allocPrint(res.allocator, "Error: {}", .{e});
-            defer res.allocator.free(errmsg);
-            // Now send an error
-            res.status = .internal_server_error;
-            res.transfer_encoding = .{ .content_length = errmsg.len };
-            try res.send();
-            try res.writeAll(errmsg);
-            try res.finish();
-        },
-        .responded, .finished => {
-            // Too late!
-            log.err("can't send an error, response already sent, state {}", .{res.state});
-        },
-    }
-}
-
-const Fixture = struct {
-    a: std.mem.Allocator,
-    port: u16,
-    t: std.Thread,
-    cancel: std.atomic.Value(bool),
-    base_url: []const u8,
-    thread_pool: std.Thread.Pool,
-    server: std.http.Server,
-    client: std.http.Client,
-
-    fn init(a: std.mem.Allocator) !*Fixture {
-        var res: *Fixture = try a.create(Fixture);
-        errdefer a.destroy(res);
-        res.a = a;
-        try std.Thread.Pool.init(&res.thread_pool, .{ .allocator = a, .n_jobs = 32 });
-        errdefer res.thread_pool.deinit();
-        res.server = std.http.Server.init(.{ .reuse_address = true, .reuse_port = true });
-        errdefer res.server.deinit();
-        const addr = std.net.Address.initIp4(.{127,0,0,1}, 0);
-        try res.server.listen(addr);
-        res.port = res.server.socket.listen_address.in.getPort();
-        res.base_url = try std.fmt.allocPrint(a, "http://localhost:{}", .{res.port});
-        errdefer a.free(res.base_url);
-        res.cancel = std.atomic.Value(bool).init(false);
-        res.t = try std.Thread.spawn(.{}, acceptLoop, .{a, &res.server, &res.thread_pool, res});
-        res.client = .{.allocator = a};
-        return res;
-    }
-
-    fn deinit(self: *Fixture) void {
-        self.client.deinit();
-        self.a.free(self.base_url);
-        self.cancel.store(true, .Unordered);
-        // Trigger the server's accept() method to wake it up
-        if (std.net.tcpConnectToAddress(std.net.Address.initIp4(.{127,0,0,1}, self.port))) |stream| {
-            stream.close();
-        } else |_| {}
-        self.t.join();
-        self.thread_pool.deinit();
-        self.server.deinit();
-        self.a.destroy(self);
-    }
-
-    fn cancelled(self: *Fixture) bool {
-        return self.cancel.load(.Unordered);
-    }
-};
-
-
-test "static pages" {
-    const a = std.testing.allocator;
-    var f = try Fixture.init(a);
-    defer f.deinit();
-    const TestCase = struct {
-        path: []const u8,
-        file: []const u8,
-    };
-    const test_cases = [_]TestCase{
-        .{ .path = "/", .file = "public/index.html"},
-        .{ .path = "/posts/2018-05-31-new-site/", .file = "public/posts/2018-05-31-new-site/index.html"},
-    };
-
-    for (test_cases) |test_case| {
-        const url = try std.fmt.allocPrint(a, "{s}{s}", .{f.base_url, test_case.path});
-        defer a.free(url);
-        var fr = try f.client.fetch(a, .{
-            .location = .{.url = url}
-            });
-        defer fr.deinit();
-        try std.testing.expectEqual(std.http.Status.ok, fr.status);
-        const expected = try std.fs.cwd().readFileAlloc(a, test_case.file, 1_000_000);
-        defer a.free(expected);
-        try std.testing.expectEqualStrings(expected, fr.body.?);
-    }
-}
-
-// test "404" {
-
-// }
-\ No newline at end of file
diff --git a/server/src/tmp.zig b/server/src/tmp.zig
@@ -1,44 +0,0 @@
-const std = @import("std");
-
-test "slices" {
-    // option 1 slices
-    const a = std.testing.allocator;
-    var words_al = std.ArrayList([]const u8).init(a);
-    defer words_al.deinit();
-    var toks = std.mem.tokenizeAny(u8, wordlist, "\n ");
-    while (toks.next()) |tok| {
-        try words_al.append(tok);
-    }
-    const words: []const[]const u8 = try words_al.toOwnedSlice(); // a slice is a pointer + length (so 2 x usize x length)
-    defer a.free(words);
-
-    try std.testing.expectEqualStrings("bar", words[1]); // words accessed by index
-}
-
-test "offsets" {
-    // option 2 offsets
-    const a = std.testing.allocator;
-    var offsets_al = std.ArrayList(u16).init(a);
-    defer offsets_al.deinit();
-    try offsets_al.append(0);
-    for (wordlist, 0..) |ch,ix| {
-        if (ch == '\n' and ix < wordlist.len) {
-            try offsets_al.append(@intCast(ix+1)); // we know we have less than 2^16 words
-        }
-    }
-    const offsets = try offsets_al.toOwnedSlice(); // offsets are just u16 x length, so ~4x less memory on 64 bit systems.
-    defer a.free(offsets);
-    // words accessed by slicing wordlist using offsets
-    // be careful of edge cases i.e. accessing the last word in the list, not handled here.
-    const word = wordlist[offsets[1]..(offsets[2]-1)]; 
-    try std.testing.expectEqualStrings("bar", word);
-}
-
-const wordlist: []const u8 = 
-\\foo
-\\bar
-\\baz
-\\I
-\\like
-\\cheese
-;