Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ frameworks/blitz/zig-linux-*
frameworks/blitz/.zig-cache
frameworks/fletch/.dart_tool/
!frameworks/hyperf/bin/
frameworks/zix/.zig-cache
frameworks/zix/zig-out
frameworks/zix-grpc/.zig-cache
frameworks/zix-grpc/zig-out

# IDE settings
*.user
Expand Down
46 changes: 46 additions & 0 deletions frameworks/zix-grpc/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# syntax=docker/dockerfile:1.7
#
# zix-grpc. Zig gRPC h2c server using the zix library.
# EPOLL dispatch: single accept loop + worker pool, EPOLLONESHOT per stream.
# Implements benchmark.BenchmarkService/GetSum (unary) and StreamSum (server-streaming).
# Built statically against musl so the runtime image is a single binary on scratch.

FROM debian:bookworm-slim AS build
ARG ZIG_VERSION=0.16.0
ARG TARGETARCH
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl xz-utils \
&& rm -rf /var/lib/apt/lists/*

RUN set -eu; \
case "${TARGETARCH:-amd64}" in \
amd64) ZIG_ARCH=x86_64 ;; \
arm64) ZIG_ARCH=aarch64 ;; \
*) echo "unsupported arch: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-${ZIG_ARCH}-linux-${ZIG_VERSION}.tar.xz" \
| tar -xJ -C /opt; \
mv "/opt/zig-${ZIG_ARCH}-linux-${ZIG_VERSION}" /opt/zig
ENV PATH="/opt/zig:${PATH}"

# Vendor zix 0.2.x at a pinned commit, separate layer so source-only rebuilds
# skip the download. Strip the top-level directory produced by GitHub's archive.
RUN mkdir -p /src/vendor/zix && \
curl -fsSL https://github.com/prothegee/zix/archive/refs/heads/0.2.x.tar.gz \
| tar -xz --strip-components=1 -C /src/vendor/zix

WORKDIR /src
COPY build.zig build.zig.zon ./
COPY src ./src
RUN set -eu; \
case "${TARGETARCH:-amd64}" in \
amd64) ZIG_TARGET=x86_64-linux-musl ;; \
arm64) ZIG_TARGET=aarch64-linux-musl ;; \
esac; \
zig build -Dtarget="${ZIG_TARGET}" --release=fast

FROM debian:bookworm-slim
COPY --from=build /src/zig-out/bin/zix-grpc /zix-grpc
EXPOSE 8080
ENTRYPOINT ["/zix-grpc"]
26 changes: 26 additions & 0 deletions frameworks/zix-grpc/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast });

const zix_dep = b.dependency("zix", .{ .target = target, .optimize = optimize });
const zix_mod = zix_dep.module("zix");

const exe = b.addExecutable(.{
.name = "zix-grpc",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.strip = true,
}),
});
exe.root_module.addImport("zix", zix_mod);
b.installArtifact(exe);

const run_step = b.step("run", "Run the gRPC server");
const run_cmd = b.addRunArtifact(exe);
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}
16 changes: 16 additions & 0 deletions frameworks/zix-grpc/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.{
.name = .zix_grpc_arena,
.version = "0.1.0",
.fingerprint = 0x458ebf118a0a92d9,
.minimum_zig_version = "0.16.0",
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
.dependencies = .{
.zix = .{
.path = "vendor/zix",
},
},
}
14 changes: 14 additions & 0 deletions frameworks/zix-grpc/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"display_name": "zix-grpc",
"language": "Zig",
"type": "engine",
"engine": "zix",
"description": "Zig gRPC h2c server using the zix framework. Single epoll event loop; worker pool handles one stream per slot. Implements GetSum (unary) and StreamSum (server-streaming).",
"repo": "https://github.com/prothegee/zix",
"enabled": true,
"tests": [
"unary-grpc",
"stream-grpc"
],
"maintainers": ["prothegee"]
}
99 changes: 99 additions & 0 deletions frameworks/zix-grpc/src/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
const std = @import("std");
const zix = @import("zix");

// --------------------------------------------------------- //

const PORT: u16 = 8080;
/// Required for ipv4 and ipv6
const LISTEN_IP: []const u8 = "::";
const DISPATCH_MODEL: zix.Grpc.DispatchModel = .EPOLL;
const KERNEL_BACKLOG: u31 = 1024 * 16;
const WORKERS: usize = 0;
const POOL_SIZE: usize = 0;

// --------------------------------------------------------- //

/// Unary RPC: SumRequest{a, b} -> SumReply{result: a+b}
fn getSumHandler(headers: []const zix.Http2.Header, ctx: *zix.Grpc.Context) void {
_ = headers;

const msg = ctx.recvMessage() orelse {
ctx.finish(.INVALID_ARGUMENT, "empty request");
return;
};

var reader = zix.Grpc.MessageReader.init(msg);
var req_a: i32 = 0;
var req_b: i32 = 0;

while (reader.next() catch null) |field| {
switch (field.field_number) {
1 => req_a = @bitCast(@as(u32, @truncate(field.value_u64))),
2 => req_b = @bitCast(@as(u32, @truncate(field.value_u64))),
else => {},
}
}

var reply_buf: [16]u8 = undefined;
const reply_len = zix.Grpc.encodeInt32(1, req_a + req_b, &reply_buf);

ctx.sendMessage("application/grpc+proto", reply_buf[0..reply_len]);
ctx.finish(.OK, "");
}

/// Server-streaming RPC: StreamRequest{a, b, count} -> count * SumReply{result: a+b+i}
fn streamSumHandler(headers: []const zix.Http2.Header, ctx: *zix.Grpc.Context) void {
_ = headers;

const msg = ctx.recvMessage() orelse {
ctx.finish(.INVALID_ARGUMENT, "empty request");
return;
};

var reader = zix.Grpc.MessageReader.init(msg);
var req_a: i32 = 0;
var req_b: i32 = 0;
var req_count: i32 = 1;

while (reader.next() catch null) |field| {
switch (field.field_number) {
1 => req_a = @bitCast(@as(u32, @truncate(field.value_u64))),
2 => req_b = @bitCast(@as(u32, @truncate(field.value_u64))),
3 => req_count = @bitCast(@as(u32, @truncate(field.value_u64))),
else => {},
}
}

if (req_count <= 0) req_count = 1;

const sum = req_a + req_b;
var reply_buf: [16]u8 = undefined;

var i: i32 = 0;
while (i < req_count) : (i += 1) {
const reply_len = zix.Grpc.encodeInt32(1, sum + i, &reply_buf);
ctx.sendMessage("application/grpc+proto", reply_buf[0..reply_len]);
}

ctx.finish(.OK, "");
}

// --------------------------------------------------------- //

pub fn main(process: std.process.Init) !void {
var server = try zix.Grpc.Server.init(&[_]zix.Grpc.Route{
.{ .path = "/benchmark.BenchmarkService/GetSum", .handler = getSumHandler },
.{ .path = "/benchmark.BenchmarkService/StreamSum", .handler = streamSumHandler, .is_server_streaming = true },
}, .{
.io = process.io,
.ip = LISTEN_IP,
.port = PORT,
.dispatch_model = DISPATCH_MODEL,
.kernel_backlog = KERNEL_BACKLOG,
.workers = WORKERS,
.pool_size = POOL_SIZE,
});
defer server.deinit();

try server.run();
}
45 changes: 45 additions & 0 deletions frameworks/zix/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# syntax=docker/dockerfile:1.7
#
# zix. Zig HTTP/1.1 server using the zix library.
# EPOLL dispatch: single accept loop + worker pool, EPOLLONESHOT per connection.
# Built statically against musl so the runtime image is a single binary on scratch.

FROM debian:bookworm-slim AS build
ARG ZIG_VERSION=0.16.0
ARG TARGETARCH
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl xz-utils \
&& rm -rf /var/lib/apt/lists/*

RUN set -eu; \
case "${TARGETARCH:-amd64}" in \
amd64) ZIG_ARCH=x86_64 ;; \
arm64) ZIG_ARCH=aarch64 ;; \
*) echo "unsupported arch: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-${ZIG_ARCH}-linux-${ZIG_VERSION}.tar.xz" \
| tar -xJ -C /opt; \
mv "/opt/zig-${ZIG_ARCH}-linux-${ZIG_VERSION}" /opt/zig
ENV PATH="/opt/zig:${PATH}"

# Vendor zix 0.2.x at a pinned commit, separate layer so source-only rebuilds
# skip the download. Strip the top-level directory produced by GitHub's archive.
RUN mkdir -p /src/vendor/zix && \
curl -fsSL https://github.com/prothegee/zix/archive/refs/heads/0.2.x.tar.gz \
| tar -xz --strip-components=1 -C /src/vendor/zix

WORKDIR /src
COPY build.zig build.zig.zon ./
COPY src ./src
RUN set -eu; \
case "${TARGETARCH:-amd64}" in \
amd64) ZIG_TARGET=x86_64-linux-musl ;; \
arm64) ZIG_TARGET=aarch64-linux-musl ;; \
esac; \
zig build -Dtarget="${ZIG_TARGET}" --release=fast

FROM debian:bookworm-slim
COPY --from=build /src/zig-out/bin/zix /zix
EXPOSE 8080
ENTRYPOINT ["/zix"]
26 changes: 26 additions & 0 deletions frameworks/zix/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseFast });

const zix_dep = b.dependency("zix", .{ .target = target, .optimize = optimize });
const zix_mod = zix_dep.module("zix");

const exe = b.addExecutable(.{
.name = "zix",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.strip = true,
}),
});
exe.root_module.addImport("zix", zix_mod);
b.installArtifact(exe);

const run_step = b.step("run", "Run the server");
const run_cmd = b.addRunArtifact(exe);
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}
16 changes: 16 additions & 0 deletions frameworks/zix/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.{
.name = .zix_arena,
.version = "0.1.0",
.fingerprint = 0x84cceaddc218682f,
.minimum_zig_version = "0.16.0",
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
.dependencies = .{
.zix = .{
.path = "vendor/zix",
},
},
}
19 changes: 19 additions & 0 deletions frameworks/zix/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"display_name": "zix",
"language": "Zig",
"type": "engine",
"engine": "zix",
"description": "Zig HTTP/1.1 server using the zix framework. Single epoll event loop drains accepts; a fixed worker pool serves one request per epoll event with EPOLLONESHOT keep-alive.",
"repo": "https://github.com/prothegee/zix",
"enabled": true,
"tests": [
"baseline",
"pipelined",
"limited-conn",
"json",
"upload",
"static",
"echo-ws"
],
"maintainers": ["prothegee"]
}
Loading