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
42 changes: 42 additions & 0 deletions src/Servers/SwerverServer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Http11Probe target for swerver (https://github.com/justinGrosvenor/swerver),
# a high-performance HTTP/1.1+2+3 server and API gateway in Zig.
#
# Base is Debian trixie (not bookworm like the other targets): swerver's
# HTTP/3 path links OpenSSL's QUIC TLS API (SSL_set_quic_tls_transport_params),
# which is only present in OpenSSL 3.5+ — trixie ships it, bookworm (3.0) does not.
FROM debian:trixie AS build

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl xz-utils git libssl-dev zlib1g-dev dpkg-dev pkg-config \

Check warning on line 10 in src/Servers/SwerverServer/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Sort these package names alphanumerically.

See more on https://sonarcloud.io/project/issues?id=MDA2AV_Http11Probe&issues=AZ61SC2_PG6GloFE3ml6&open=AZ61SC2_PG6GloFE3ml6&pullRequest=131
&& rm -rf /var/lib/apt/lists/*

# Zig 0.16.0 stable
RUN set -eux; \
ARCH=$(dpkg --print-architecture); \
case "$ARCH" in amd64) ZA=x86_64 ;; arm64) ZA=aarch64 ;; *) echo "unsupported $ARCH" >&2; exit 1 ;; esac; \
curl -fsSL --proto '=https' --tlsv1.2 --retry 5 --retry-all-errors --retry-delay 3 --connect-timeout 30 "https://ziglang.org/download/0.16.0/zig-${ZA}-linux-0.16.0.tar.xz" -o /tmp/zig.tar.xz; \
mkdir -p /opt/zig; tar -xJf /tmp/zig.tar.xz -C /opt/zig --strip-components=1; \
ln -s /opt/zig/zig /usr/local/bin/zig; zig version

# Clone swerver, then place the probe app at <swerver>/probeapp so its
# build.zig.zon `.path = ".."` dependency resolves to the swerver tree.
WORKDIR /src
RUN git clone --depth 1 --branch main https://github.com/justinGrosvenor/swerver.git .
RUN MULTIARCH=$(dpkg-architecture -qDEB_HOST_MULTIARCH); \
for lib in libssl.so libssl.a libcrypto.so libcrypto.a; do \
ln -sf "/usr/lib/${MULTIARCH}/${lib}" "/usr/lib/${lib}"; \
done
COPY src/Servers/SwerverServer/main.zig src/Servers/SwerverServer/build.zig src/Servers/SwerverServer/build.zig.zon /src/probeapp/
WORKDIR /src/probeapp
RUN zig build --summary all

# ── runtime ──
FROM debian:trixie-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates libssl3 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=build /src/probeapp/zig-out/bin/swerver-probe /usr/local/bin/swerver-probe
COPY src/Servers/SwerverServer/config.json /app/config.json
COPY src/Servers/SwerverServer/docroot/ /app/docroot/
USER nobody
EXPOSE 8080
CMD ["/usr/local/bin/swerver-probe", "--config", "/app/config.json"]
12 changes: 12 additions & 0 deletions src/Servers/SwerverServer/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize: std.builtin.OptimizeMode = .ReleaseFast;
const dep = b.dependency("swerver", .{ .target = target, .optimize = optimize,
.@"enable-tls" = true, .@"enable-http2" = true, .@"enable-http3" = true });
const m = b.createModule(.{ .root_source_file = b.path("main.zig"),
.target = target, .optimize = optimize, .link_libc = true });
m.addImport("swerver", dep.module("swerver"));
const exe = b.addExecutable(.{ .name = "swerver-probe", .root_module = m });
b.installArtifact(exe);
}
8 changes: 8 additions & 0 deletions src/Servers/SwerverServer/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.{
.name = .swerver_probe,
.version = "0.1.0",
.fingerprint = 0x746a6fb0ecdcc9b,
.minimum_zig_version = "0.16.0",
.paths = .{ "build.zig", "build.zig.zon", "main.zig" },
.dependencies = .{ .swerver = .{ .path = ".." } },
}
1 change: 1 addition & 0 deletions src/Servers/SwerverServer/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "server": { "address": "0.0.0.0", "port": 8080, "workers": 1, "static_root": "/app/docroot" } }
1 change: 1 addition & 0 deletions src/Servers/SwerverServer/docroot/index.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello from swerver static probe target. This file exists to exercise conditional and range requests.
87 changes: 87 additions & 0 deletions src/Servers/SwerverServer/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Http11Probe target for swerver. Mirrors the reference servers' endpoint
// contract (see NginxServer/echo.js): GET / -> "OK", POST / -> echo body,
// /echo -> request headers dumped, /cookie -> parsed cookies. A static
// docroot backs the conditional/range probe tests.
const std = @import("std");
const swerver = @import("swerver");
const router = swerver.router;
const response_mod = swerver.response;

fn handleRoot(ctx: *router.HandlerContext) response_mod.Response {
if (ctx.request.method == .POST) {
const body = ctx.request.body.sliceOrNull() orelse "";
return .{ .status = 200, .headers = &[_]response_mod.Header{
.{ .name = "Content-Type", .value = "text/plain" },
}, .body = .{ .bytes = body } };
}
return .{ .status = 200, .headers = &[_]response_mod.Header{
.{ .name = "Content-Type", .value = "text/plain" },
}, .body = .{ .bytes = "OK" } };
}

fn handleEcho(ctx: *router.HandlerContext) response_mod.Response {
var off: usize = 0;
const buf = ctx.response_buf;
for (ctx.request.headers) |h| {
const line = std.fmt.bufPrint(buf[off..], "{s}: {s}\n", .{ h.name, h.value }) catch break;
off += line.len;
}
return .{ .status = 200, .headers = &[_]response_mod.Header{
.{ .name = "Content-Type", .value = "text/plain" },
}, .body = .{ .bytes = buf[0..off] } };
}

fn handleCookie(ctx: *router.HandlerContext) response_mod.Response {
var off: usize = 0;
const buf = ctx.response_buf;
if (ctx.request.getHeader("cookie")) |raw| {
var it = std.mem.splitScalar(u8, raw, ';');
while (it.next()) |pair| {
const trimmed = std.mem.trim(u8, pair, " \t");
if (std.mem.indexOfScalar(u8, trimmed, '=')) |eq| {
if (eq > 0) {
const line = std.fmt.bufPrint(buf[off..], "{s}={s}\n", .{ trimmed[0..eq], trimmed[eq + 1 ..] }) catch break;
off += line.len;
}
}
}
}
return .{ .status = 200, .headers = &[_]response_mod.Header{
.{ .name = "Content-Type", .value = "text/plain" },
}, .body = .{ .bytes = buf[0..off] } };
}

pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
var loaded: ?swerver.config_file.LoadedConfig = null;
defer if (loaded) |*lc| lc.deinit();
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
defer args.deinit();
_ = args.next();
var config_path: ?[]const u8 = null;
while (args.next()) |a| {
const arg = std.mem.sliceTo(a, 0);
if (std.mem.eql(u8, arg, "--config")) {
if (args.next()) |v| config_path = std.mem.sliceTo(v, 0);
}
}
var cfg: swerver.config.ServerConfig = blk: {
if (config_path) |p| {
loaded = try swerver.config_file.loadConfigFile(allocator, p);
break :blk loaded.?.server_config;
}
break :blk swerver.config.ServerConfig.default();
};
try cfg.validate();

var app = router.Router.init(.{});
try app.get("/", handleRoot);
try app.post("/", handleRoot);
try app.get("/echo", handleEcho);
try app.post("/echo", handleEcho);
try app.get("/cookie", handleCookie);

const srv = try swerver.ServerBuilder.config(cfg).router(app).disablePreencoded().build(allocator);
defer { srv.deinit(); allocator.destroy(srv); }
try srv.run(null);
}
1 change: 1 addition & 0 deletions src/Servers/SwerverServer/probe.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "name": "Swerver", "language": "Zig" }
Loading