Browse Source

Add Zig zap framework (#8791)

* Zig zap framework

* Zig zap framework db and fortune tests

* Zig zap framework fortunes
Dragos Varovici 1 year ago
parent
commit
63f79fbb91

+ 2 - 0
frameworks/Zig/zap/.gitignore

@@ -0,0 +1,2 @@
+zig-cache/**/*',
+zig-out: 'zig-out/**/*',

+ 25 - 0
frameworks/Zig/zap/README.md

@@ -0,0 +1,25 @@
+
+# [Zap](https://github.com/zigzap/zap) - Blazingly fast backends in zig
+
+## Description
+
+Zap is the zig microframework for web applications.
+
+## Test URLs
+
+### Test 1: JSON Encoding
+
+    http://localhost:3000/json
+
+### Test 2: Plaintext
+
+    http://localhost:3000/plaintext
+
+### Test 2: Single Row Query
+
+    http://localhost:3000/db
+
+### Test 4: Fortunes (Template rendering)
+
+    http://localhost:3000/fortunes
+

+ 26 - 0
frameworks/Zig/zap/benchmark_config.json

@@ -0,0 +1,26 @@
+{
+  "framework": "zap",
+  "tests": [{
+    "default": {
+      "json_url": "/json",
+      "db_url": "/db",
+      "fortune_url": "/fortunes",
+      "plaintext_url": "/plaintext",
+      "port": 3000,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "Zap",
+      "language": "Zig",
+      "flavor": "None",
+      "orm": "Full",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Zap (Zig)",
+      "notes": "",
+      "versus": ""
+    }
+  }]
+}

+ 99 - 0
frameworks/Zig/zap/build.zig

@@ -0,0 +1,99 @@
+const std = @import("std");
+
+// Although this function looks imperative, note that its job is to
+// declaratively construct a build graph that will be executed by an external
+// runner.
+pub fn build(b: *std.Build) void {
+    // Standard target options allows the person running `zig build` to choose
+    // what target to build for. Here we do not override the defaults, which
+    // means any target is allowed, and the default is native. Other options
+    // for restricting supported target set are available.
+    const target = b.standardTargetOptions(.{});
+
+    // Standard optimization options allow the person running `zig build` to select
+    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do nots
+    // set a preferred release mode, allowing the user to decide how to optimize.
+    const optimize = b.standardOptimizeOption(.{});
+
+    const exe = b.addExecutable(.{
+        .name = "zap",
+        // In this case the main source file is merely a path, however, in more
+        // complicated build scripts, this could be a generated file.
+        .root_source_file = .{ .path = "src/main.zig" },
+        .target = target,
+        .optimize = optimize,
+    });
+
+    //exe.addPackagePath("random", "src/random.zig");
+
+    const zap = b.dependency("zap", .{
+        .target = target,
+        .optimize = optimize,
+        .openssl = false, // set to true to enable TLS support
+    });
+    exe.addModule("zap", zap.module("zap"));
+
+    const pg = b.dependency("pg", .{
+        .target = target,
+        .optimize = optimize,
+    });
+    exe.addModule("pg", pg.module("pg"));
+
+    const dig = b.dependency("dig", .{
+    .target = target,
+    .optimize = optimize,
+    });
+    exe.addModule("dns", dig.module("dns"));
+
+    // const mustache = b.dependency("mustache", .{
+    //     .target = target,
+    //     .optimize = optimize,
+    // });
+    // exe.addModule("mustache", mustache.module("mustache"));
+
+    exe.linkLibrary(zap.artifact("facil.io"));
+
+    // This declares intent for the executable to be installed into the
+    // standard location when the user invokes the "install" step (the default
+    // step when running `zig build`).
+    b.installArtifact(exe);
+
+    // This *creates* a Run step in the build graph, to be executed when another
+    // step is evaluated that depends on it. The next line below will establish
+    // such a dependency.
+    const run_cmd = b.addRunArtifact(exe);
+
+    // By making the run step depend on the install step, it will be run from the
+    // installation directory rather than directly from within the cache directory.
+    // This is not necessary, however, if the application depends on other installed
+    // files, this ensures they will be present and in the expected location.
+    run_cmd.step.dependOn(b.getInstallStep());
+
+    // This allows the user to pass arguments to the application in the build
+    // command itself, like this: `zig build run -- arg1 arg2 etc`
+    if (b.args) |args| {
+        run_cmd.addArgs(args);
+    }
+
+    // This creates a build step. It will be visible in the `zig build --help` menu,
+    // and can be selected like this: `zig build run`
+    // This will evaluate the `run` step rather than the default, which is "install".
+    const run_step = b.step("run", "Run the app");
+    run_step.dependOn(&run_cmd.step);
+
+    // Creates a step for unit testing. This only builds the test executable
+    // but does not run it.
+    const unit_tests = b.addTest(.{
+        .root_source_file = .{ .path = "src/main.zig" },
+        .target = target,
+        .optimize = optimize,
+    });
+
+    const run_unit_tests = b.addRunArtifact(unit_tests);
+
+    // Similar to creating the run step earlier, this exposes a `test` step to
+    // the `zig build --help` menu, providing a way for the user to request
+    // running the unit tests.
+    const test_step = b.step("test", "Run unit tests");
+    test_step.dependOn(&run_unit_tests.step);
+}

+ 21 - 0
frameworks/Zig/zap/build.zig.zon

@@ -0,0 +1,21 @@
+.{
+    .name = "Zap testing",
+    .version = "0.1.0",
+
+    .dependencies = .{
+        // zap v0.5.1
+        .zap = .{
+            .url = "https://github.com/zigzap/zap/archive/refs/tags/v0.5.1.tar.gz",
+            .hash = "1220d4802fb09d4e99c0e7265f90d6f3cfdc3e5e31c1b05f0924ee2dd26d9d6dbbf4",
+        },
+        .pg = .{
+            .url = "https://github.com/karlseguin/pg.zig/archive/f3f4a0b3b9996bfb1bf9bd0bdd0d73b36e915a86.tar.gz",
+            .hash = "1220337202642ee66408a35f254549f22cf3a096c6fa6c28e6f87a0161d5a6c0f4ab"
+        },
+        .dig = .{
+            .url = "https://github.com/lun-4/zigdig/archive/2ec407ec3c7f347e747717977958e9ba339eb82f.tar.gz",
+            .hash = "1220dfdb3089dfe9a4e4bc1226fcff08d91d0c0853f287d98d8b81270da251790331"
+        },
+
+    }
+}

+ 3 - 0
frameworks/Zig/zap/run.sh

@@ -0,0 +1,3 @@
+echo "Waiting for ZAP to start..."
+
+zap

+ 334 - 0
frameworks/Zig/zap/src/endpoints.zig

@@ -0,0 +1,334 @@
+const std = @import("std");
+const zap = @import("zap");
+const pg = @import("pg");
+
+const Mustache = @import("zap").Mustache;
+const Thread = std.Thread;
+const Mutex = Thread.Mutex;
+
+const middleware = @import("middleware.zig");
+
+const Message = struct {
+    message: []const u8,
+};
+
+const World = struct {
+    id: i32,
+    randomNumber: i32,
+};
+
+const Fortune = struct {
+    id: i32,
+    message: []const u8,
+};
+
+pub const FortunesEndpoint = struct {
+    ep: zap.Endpoint = undefined,
+    mustache: Mustache,
+    mutex: Mutex,
+
+    const Self = @This();
+
+    pub fn init() Self {
+        const template = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>{{#fortunes}}<tr><td>{{id}}</td><td>{{message}}</td></tr>{{/fortunes}}</table></body></html>";
+        const mustache = Mustache.fromData(template) catch unreachable;
+
+        return .{
+            .ep = zap.Endpoint.init(.{
+                .path = "/fortunes",
+                .get = get,
+            }),
+            .mustache = mustache,
+            .mutex = Mutex{},
+        };
+    }
+
+    pub fn deinit(self: *Self) void {
+        self.mustache.deinit();
+    }
+
+    pub fn endpoint(self: *Self) *zap.Endpoint {
+        return &self.ep;
+    }
+
+    fn compareStrings(_: void, lhs: []const u8, rhs: []const u8) bool {
+        return std.mem.order(u8, lhs, rhs).compare(std.math.CompareOperator.lt);
+    }
+
+    fn cmpFortuneByMessage(_: void, a: Fortune, b: Fortune) bool {
+        return std.mem.order(u8, a.message, b.message).compare(std.math.CompareOperator.lt);
+    }
+
+    fn getFortunes(pool: *pg.Pool) ![]const Fortune {
+        var conn = try pool.acquire();
+        defer conn.release();
+
+        var rows = try conn.query("SELECT id, message FROM Fortune", .{});
+        rows.deinit();
+
+        var fortunes = std.ArrayList(Fortune).init(middleware.SharedAllocator.getAllocator());
+        defer fortunes.deinit();
+
+        while (try rows.next()) |row| {
+            var fortune = Fortune{ .id = row.get(i32, 0), .message = row.get([]const u8, 1) };
+            _ = try fortunes.append(fortune);
+        }
+
+        var fortune = Fortune{ .id = 0, .message = "Additional fortune added at request time." };
+        _ = try fortunes.append(fortune);
+
+        var fortunes_slice = try fortunes.toOwnedSlice();
+        std.mem.sort(Fortune, fortunes_slice, {}, cmpFortuneByMessage);
+
+        return fortunes_slice;
+    }
+
+    fn getFortunesHtml(self: *Self, pool: *pg.Pool) ![]const u8 {
+        var fortunes = try getFortunes(pool);
+
+        self.mutex.lock();
+        const ret = self.mustache.build(.{ .fortunes = fortunes });
+        defer ret.deinit();
+        self.mutex.unlock();
+
+        const raw = ret.str().?;
+
+        // std.debug.print("mustache output {s}\n", .{raw});
+
+        var html = try deescapeHtml(raw);
+
+        // std.debug.print("html output {s}\n", .{html});
+
+        return html;
+    }
+
+    pub fn get(ep: *zap.Endpoint, req: zap.Request) void {
+        const self = @fieldParentPtr(Self, "ep", ep);
+
+        if (!checkPath(ep, req)) return;
+
+        req.setHeader("content-type", "text/html; charset=utf-8") catch return;
+
+        var pool: *pg.Pool = undefined;
+
+        const maybe_context: ?*middleware.Context = req.getUserContext(middleware.Context);
+        if (maybe_context) |context| {
+            if (context.pg) |cpg| {
+                pool = cpg.pool;
+            }
+        }
+
+        var fortunes_html = getFortunesHtml(self, pool) catch return;
+
+        req.sendBody(fortunes_html) catch return;
+
+        return;
+    }
+};
+
+pub const DbEndpoint = struct {
+    ep: zap.Endpoint = undefined,
+    mutex: Mutex,
+    const Self = @This();
+
+    pub fn init() Self {
+        return .{
+            .ep = zap.Endpoint.init(.{
+                .path = "/db",
+                .get = get,
+            }),
+            .mutex = Mutex{},
+        };
+    }
+
+    pub fn endpoint(self: *Self) *zap.Endpoint {
+        return &self.ep;
+    }
+
+    pub fn get(ep: *zap.Endpoint, req: zap.Request) void {
+        const self = @fieldParentPtr(Self, "ep", ep);
+
+        if (!checkPath(ep, req)) return;
+
+        req.setContentType(.JSON) catch return;
+
+        var random_number: u32 = 0;
+        var pool: *pg.Pool = undefined;
+
+        const maybe_context: ?*middleware.Context = req.getUserContext(middleware.Context);
+        if (maybe_context) |context| {
+            if (context.prng) |prng| {
+                if (context.pg) |cpg| {
+                    pool = cpg.pool;
+
+                    self.mutex.lock();
+                    random_number = 1 + (prng.rnd.random().uintAtMost(u32, 9999));
+                    self.mutex.unlock();
+                }
+            }
+        }
+
+        // std.debug.print("Attempting to return random: {}\n", .{random_number});
+
+        if (random_number == 0) {
+            return;
+        }
+
+        var conn = pool.acquire() catch return;
+        defer conn.release();
+
+        var row_result = conn.row("SELECT id, randomNumber FROM World WHERE id = $1", .{random_number}) catch |err| {
+            std.debug.print("Error querying database: {}\n", .{err});
+            return;
+        };
+        var row = row_result.?;
+        defer row.deinit();
+
+        var world = World{ .id = row.get(i32, 0), .randomNumber = row.get(i32, 1) };
+
+        var buf: [100]u8 = undefined;
+        var json_to_send: []const u8 = undefined;
+        if (zap.stringifyBuf(&buf, world, .{})) |json_message| {
+            json_to_send = json_message;
+        } else {
+            json_to_send = "null";
+        }
+
+        req.sendBody(json_to_send) catch return;
+
+        return;
+    }
+};
+
+pub const PlaintextEndpoint = struct {
+    ep: zap.Endpoint = undefined,
+    const Self = @This();
+
+    pub fn init() Self {
+        return .{
+            .ep = zap.Endpoint.init(.{
+                .path = "/plaintext",
+                .get = get,
+            }),
+        };
+    }
+
+    pub fn endpoint(self: *Self) *zap.Endpoint {
+        return &self.ep;
+    }
+
+    pub fn get(ep: *zap.Endpoint, req: zap.Request) void {
+        const self = @fieldParentPtr(Self, "ep", ep);
+        _ = self;
+
+        if (!checkPath(ep, req)) return;
+
+        req.setContentType(.TEXT) catch return;
+
+        req.sendBody("Hello, World!") catch return;
+        return;
+    }
+};
+
+pub const JsonEndpoint = struct {
+    ep: zap.Endpoint = undefined,
+    const Self = @This();
+
+    pub fn init() Self {
+        return .{
+            .ep = zap.Endpoint.init(.{
+                .path = "/json",
+                .get = get,
+            }),
+        };
+    }
+
+    pub fn endpoint(self: *Self) *zap.Endpoint {
+        return &self.ep;
+    }
+
+    pub fn get(ep: *zap.Endpoint, req: zap.Request) void {
+        const self = @fieldParentPtr(Self, "ep", ep);
+        _ = self;
+
+        if (!checkPath(ep, req)) return;
+
+        req.setContentType(.JSON) catch return;
+
+        var message = Message{ .message = "Hello, World!" };
+
+        var buf: [100]u8 = undefined;
+        var json_to_send: []const u8 = undefined;
+        if (zap.stringifyBuf(&buf, message, .{})) |json_message| {
+            json_to_send = json_message;
+        } else {
+            json_to_send = "null";
+        }
+
+        req.sendBody(json_to_send) catch return;
+        return;
+    }
+};
+
+fn checkPath(ep: *zap.Endpoint, req: zap.Request) bool {
+    if (!std.mem.eql(u8, ep.settings.path, req.path.?)) {
+        // std.debug.print("Path mismatch: {s} != {s}\n", .{ ep.settings.path, req.path.? });
+
+        return false;
+    }
+
+    // std.debug.print("Path match: {s} == {s}\n", .{ ep.settings.path, req.path.? });
+
+    return true;
+}
+
+fn deescapeHtml(input: []const u8) ![]const u8 {
+    var output = std.ArrayList(u8).init(middleware.SharedAllocator.getAllocator());
+    defer output.deinit();
+
+    var i: usize = 0;
+    while (i < input.len) {
+        if (std.mem.startsWith(u8, input[i..], "&#32;")) {
+            try output.append(' ');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#34;")) {
+            try output.append('"');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#38;")) {
+            try output.append('&');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#39;")) {
+            try output.append('\'');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#40;")) {
+            try output.append('(');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#41;")) {
+            try output.append(')');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#43;")) {
+            try output.append('+');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#44;")) {
+            try output.append(',');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#46;")) {
+            try output.append('.');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#47;")) {
+            try output.append('/');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#58;")) {
+            try output.append(':');
+            i += 5;
+        } else if (std.mem.startsWith(u8, input[i..], "&#59;")) {
+            try output.append(';');
+            i += 5;
+        } else {
+            try output.append(input[i]);
+            i += 1;
+        }
+    }
+
+    return output.toOwnedSlice();
+}

+ 95 - 0
frameworks/Zig/zap/src/main.zig

@@ -0,0 +1,95 @@
+const std = @import("std");
+const zap = @import("zap");
+const pg = @import("pg");
+const regex = @import("regex");
+const dns = @import("dns");
+const pool = @import("pool.zig");
+
+const endpoints = @import("endpoints.zig");
+const middleware = @import("middleware.zig");
+
+const RndGen = std.rand.DefaultPrng;
+const Allocator = std.mem.Allocator;
+const Pool = pg.Pool;
+
+pub fn main() !void {
+    var gpa = std.heap.GeneralPurposeAllocator(.{
+        .thread_safe = true,
+    }){};
+
+    var tsa = std.heap.ThreadSafeAllocator{
+        .child_allocator = gpa.allocator(),
+    };
+
+    var allocator = tsa.allocator();
+
+    var pg_pool = try pool.initPool(allocator);
+    defer pg_pool.deinit();
+
+    var rnd = std.rand.DefaultPrng.init(blk: {
+        var seed: u64 = undefined;
+        try std.os.getrandom(std.mem.asBytes(&seed));
+        break :blk seed;
+    });
+
+    middleware.SharedAllocator.init(allocator);
+
+    // create the endpoint
+    var dbEndpoint = endpoints.DbEndpoint.init();
+    var plaintextEndpoint = endpoints.PlaintextEndpoint.init();
+    var jsonEndpoint = endpoints.JsonEndpoint.init();
+    var fortunesEndpoint = endpoints.FortunesEndpoint.init();
+
+    // we wrap the endpoint with a middleware handler
+    var jsonEndpointHandler = zap.Middleware.EndpointHandler(middleware.Handler, middleware.Context).init(
+        jsonEndpoint.endpoint(), // the endpoint
+        null, // no other handler (we are the last in the chain)
+        false, // break on finish. See EndpointHandler for this. Not applicable here.
+    );
+
+    var plaintextEndpointHandler = zap.Middleware.EndpointHandler(middleware.Handler, middleware.Context).init(
+        plaintextEndpoint.endpoint(),
+        jsonEndpointHandler.getHandler(),
+        false,
+    );
+
+    var fortunesEndpointHandler = zap.Middleware.EndpointHandler(middleware.Handler, middleware.Context).init(
+        fortunesEndpoint.endpoint(), // the endpoint
+        plaintextEndpointHandler.getHandler(), // no other handler (we are the last in the chain)
+        false,
+    );
+
+    var dbEndpointHandler = zap.Middleware.EndpointHandler(middleware.Handler, middleware.Context).init(
+        dbEndpoint.endpoint(), // the endpoint
+        fortunesEndpointHandler.getHandler(), // no other handler (we are the last in the chain)
+        false,
+    );
+
+    var headerHandler = middleware.HeaderMiddleWare.init(dbEndpointHandler.getHandler());
+    var prngHandler = middleware.PrngMiddleWare.init(headerHandler.getHandler(), &rnd);
+    var pgHandler = middleware.PgMiddleWare.init(prngHandler.getHandler(), pg_pool);
+
+    var listener = try zap.Middleware.Listener(middleware.Context).init(
+        .{
+            .on_request = null, // must be null
+            .port = 3000,
+            .log = false,
+            .max_clients = 100000,
+        },
+        pgHandler.getHandler(),
+
+        middleware.SharedAllocator.getAllocator,
+    );
+    try listener.listen();
+
+    const cpuCount = @as(i16, @intCast(std.Thread.getCpuCount() catch 1));
+
+    std.debug.print("Listening on 0.0.0.0:3000 on {d} threads\n", .{cpuCount});
+
+    // start worker threads
+    zap.start(.{
+        .threads = 16 * cpuCount,
+        .workers = 1,
+    });
+}
+

+ 129 - 0
frameworks/Zig/zap/src/middleware.zig

@@ -0,0 +1,129 @@
+const std = @import("std");
+const zap = @import("zap");
+const pg = @import("pg");
+
+// just a way to share our allocator via callback
+pub const SharedAllocator = struct {
+    // static
+    var allocator: std.mem.Allocator = undefined;
+
+    const Self = @This();
+
+    // just a convenience function
+    pub fn init(a: std.mem.Allocator) void {
+        allocator = a;
+    }
+
+    // static function we can pass to the listener later
+    pub fn getAllocator() std.mem.Allocator {
+        return allocator;
+    }
+};
+
+// create a combined context struct
+pub const Context = struct {
+    prng: ?PrngMiddleWare.Prng = null,
+    pg: ?PgMiddleWare.Pg = null,
+};
+
+pub const Handler = zap.Middleware.Handler(Context);
+
+pub const HeaderMiddleWare = struct {
+    handler: Handler,
+
+    const Self = @This();
+
+    pub fn init(other: ?*Handler) Self {
+        return .{
+            .handler = Handler.init(onRequest, other),
+        };
+    }
+
+    // we need the handler as a common interface to chain stuff
+    pub fn getHandler(self: *Self) *Handler {
+        return &self.handler;
+    }
+
+    // note that the first parameter is of type *Handler, not *Self !!!
+    pub fn onRequest(handler: *Handler, req: zap.Request, context: *Context) bool {
+        // this is how we would get our self pointer
+        var self = @fieldParentPtr(Self, "handler", handler);
+        _ = self;
+
+        req.setHeader("Server", "Zap") catch return false;
+
+        // continue in the chain
+        return handler.handleOther(req, context);
+    }
+};
+
+pub const PrngMiddleWare = struct {
+    handler: Handler,
+    rnd: *std.rand.DefaultPrng,
+
+    const Self = @This();
+
+    const Prng = struct {
+        rnd: *std.rand.DefaultPrng = undefined,
+    };
+
+    pub fn init(other: ?*Handler, rnd: *std.rand.DefaultPrng) Self {
+        return .{
+            .handler = Handler.init(onRequest, other),
+            .rnd = rnd,
+        };
+    }
+
+    // we need the handler as a common interface to chain stuff
+    pub fn getHandler(self: *Self) *Handler {
+        return &self.handler;
+    }
+
+    // note that the first parameter is of type *Handler, not *Self !!!
+    pub fn onRequest(handler: *Handler, req: zap.Request, context: *Context) bool {
+
+        // this is how we would get our self pointer
+        var self = @fieldParentPtr(Self, "handler", handler);
+
+        context.prng = Prng{ .rnd = self.rnd };
+
+        // continue in the chain
+        return handler.handleOther(req, context);
+    }
+};
+
+pub const PgMiddleWare = struct {
+    handler: Handler,
+    pool: *pg.Pool,
+
+    const Self = @This();
+
+    const Pg = struct {
+        pool: *pg.Pool = undefined,
+    };
+
+    pub fn init(other: ?*Handler, pool: *pg.Pool) Self {
+        return .{
+            .handler = Handler.init(onRequest, other),
+            .pool = pool,
+        };
+    }
+
+    // we need the handler as a common interface to chain stuff
+    pub fn getHandler(self: *Self) *Handler {
+        return &self.handler;
+    }
+
+    // note that the first parameter is of type *Handler, not *Self !!!
+    pub fn onRequest(handler: *Handler, req: zap.Request, context: *Context) bool {
+
+        // this is how we would get our self pointer
+        var self = @fieldParentPtr(Self, "handler", handler);
+
+        // do our work: fill in the user field of the context
+        context.pg = Pg{ .pool = self.pool };
+
+        // continue in the chain
+        return handler.handleOther(req, context);
+    }
+};

+ 78 - 0
frameworks/Zig/zap/src/pool.zig

@@ -0,0 +1,78 @@
+const std = @import("std");
+const pg = @import("pg");
+const regex = @import("regex");
+const dns = @import("dns");
+
+const Allocator = std.mem.Allocator;
+const Pool = pg.Pool;
+const ArrayList = std.ArrayList;
+const Regex = regex.Regex;
+
+pub fn initPool(allocator: Allocator) !*pg.Pool {
+    const info = try parsePostgresConnStr();
+    std.debug.print("Cconnection info: {s}:{s}@{s}:{d}/{s}\n", .{ info.username, info.password, info.hostname, info.port, info.database });
+
+    const hostname = std.os.getenv("PG_HOST") orelse "localhost";
+    var addresses = try dns.helpers.getAddressList(hostname, allocator);
+    defer addresses.deinit();
+
+    var hostAddress = std.net.Address.parseIp("127.0.0.1", 0) catch unreachable;
+
+    for (addresses.addrs) |address| {
+        hostAddress = address;
+    }
+
+    std.debug.print("tfb hostname {}\n", .{hostAddress.in});
+
+    const host = try addressAsString(hostAddress);
+
+    var pg_pool = try Pool.init(allocator, .{ .size = 28, .connect = .{
+        .port = info.port,
+        .host = host,
+    }, .auth = .{
+        .username = info.username,
+        .database = info.database,
+        .password = info.password,
+    }, .timeout = 10_000,});
+
+    return pg_pool;
+}
+
+pub const ConnectionInfo = struct {
+    username: []const u8,
+    password: []const u8,
+    hostname: []const u8,
+    port: u16,
+    database: []const u8,
+};
+
+fn addressAsString(address: std.net.Address) ![]const u8 {
+    const bytes = @as(*const [4]u8, @ptrCast(&address.in.sa.addr));
+
+    var buffer: [256]u8 = undefined;
+    var source = std.io.StreamSource{ .buffer = std.io.fixedBufferStream(&buffer) };
+    var writer = source.writer();
+
+    //try writer.writeAll("Hello, World!");
+
+    try writer.print("{}.{}.{}.{}", .{
+        bytes[0],
+        bytes[1],
+        bytes[2],
+        bytes[3],
+    });
+
+    const output = source.buffer.getWritten();
+
+    return output;
+}
+
+fn parsePostgresConnStr() !ConnectionInfo {
+    return ConnectionInfo{
+        .username = std.os.getenv("PG_USER") orelse "benchmarkdbuser",
+        .password = std.os.getenv("PG_PASS") orelse "benchmarkdbpass",
+        .hostname = std.os.getenv("PG_HOST") orelse "localhost", // ,
+        .port = try std.fmt.parseInt(u16, std.os.getenv("PG_PORT") orelse "5432", 0),
+        .database = std.os.getenv("PG_DB") orelse "hello_world",
+    };
+}

+ 42 - 0
frameworks/Zig/zap/zap.dockerfile

@@ -0,0 +1,42 @@
+#FROM ziglang/static-base:llvm15-aarch64-3 as build
+FROM buddyspencer/ziglang:0.11.0-r3 as build
+
+WORKDIR /zap
+
+COPY src src
+
+COPY build.zig.zon build.zig.zon
+COPY build.zig build.zig
+
+RUN apk update
+RUN apk add yaml-dev sqlite-dev
+RUN apk add bind-tools
+RUN apk add --no-cache bash
+RUN dig +short localhost | head -n 1
+RUN zig build -Doptimize=ReleaseFast --prefix-exe-dir /usr/bin
+RUN zig version
+RUN ls
+
+EXPOSE 3000
+
+CMD ["sh", "run.sh"]
+
+FROM alpine:3.19
+
+WORKDIR /zap
+
+ENV PG_USER=benchmarkdbuser
+ENV PG_PASS=benchmarkdbpass
+ENV PG_DB=hello_world
+ENV PG_HOST=tfb-database
+ENV PG_PORT=5432
+
+COPY run.sh run.sh
+
+RUN apk update
+
+COPY --from=build /usr/bin/zap /usr/bin/zap
+
+EXPOSE 3000
+
+CMD ["sh", "run.sh"]