|
| 1 | +// Http11Probe target for swerver. Mirrors the reference servers' endpoint |
| 2 | +// contract (see NginxServer/echo.js): GET / -> "OK", POST / -> echo body, |
| 3 | +// /echo -> request headers dumped, /cookie -> parsed cookies. A static |
| 4 | +// docroot backs the conditional/range probe tests. |
| 5 | +const std = @import("std"); |
| 6 | +const swerver = @import("swerver"); |
| 7 | +const router = swerver.router; |
| 8 | +const response_mod = swerver.response; |
| 9 | + |
| 10 | +fn handleRoot(ctx: *router.HandlerContext) response_mod.Response { |
| 11 | + if (ctx.request.method == .POST) { |
| 12 | + const body = ctx.request.body.sliceOrNull() orelse ""; |
| 13 | + return .{ .status = 200, .headers = &[_]response_mod.Header{ |
| 14 | + .{ .name = "Content-Type", .value = "text/plain" }, |
| 15 | + }, .body = .{ .bytes = body } }; |
| 16 | + } |
| 17 | + return .{ .status = 200, .headers = &[_]response_mod.Header{ |
| 18 | + .{ .name = "Content-Type", .value = "text/plain" }, |
| 19 | + }, .body = .{ .bytes = "OK" } }; |
| 20 | +} |
| 21 | + |
| 22 | +fn handleEcho(ctx: *router.HandlerContext) response_mod.Response { |
| 23 | + var off: usize = 0; |
| 24 | + const buf = ctx.response_buf; |
| 25 | + for (ctx.request.headers) |h| { |
| 26 | + const line = std.fmt.bufPrint(buf[off..], "{s}: {s}\n", .{ h.name, h.value }) catch break; |
| 27 | + off += line.len; |
| 28 | + } |
| 29 | + return .{ .status = 200, .headers = &[_]response_mod.Header{ |
| 30 | + .{ .name = "Content-Type", .value = "text/plain" }, |
| 31 | + }, .body = .{ .bytes = buf[0..off] } }; |
| 32 | +} |
| 33 | + |
| 34 | +fn handleCookie(ctx: *router.HandlerContext) response_mod.Response { |
| 35 | + var off: usize = 0; |
| 36 | + const buf = ctx.response_buf; |
| 37 | + if (ctx.request.getHeader("cookie")) |raw| { |
| 38 | + var it = std.mem.splitScalar(u8, raw, ';'); |
| 39 | + while (it.next()) |pair| { |
| 40 | + const trimmed = std.mem.trim(u8, pair, " \t"); |
| 41 | + if (std.mem.indexOfScalar(u8, trimmed, '=')) |eq| { |
| 42 | + if (eq > 0) { |
| 43 | + const line = std.fmt.bufPrint(buf[off..], "{s}={s}\n", .{ trimmed[0..eq], trimmed[eq + 1 ..] }) catch break; |
| 44 | + off += line.len; |
| 45 | + } |
| 46 | + } |
| 47 | + } |
| 48 | + } |
| 49 | + return .{ .status = 200, .headers = &[_]response_mod.Header{ |
| 50 | + .{ .name = "Content-Type", .value = "text/plain" }, |
| 51 | + }, .body = .{ .bytes = buf[0..off] } }; |
| 52 | +} |
| 53 | + |
| 54 | +pub fn main(init: std.process.Init) !void { |
| 55 | + const allocator = init.gpa; |
| 56 | + var loaded: ?swerver.config_file.LoadedConfig = null; |
| 57 | + defer if (loaded) |*lc| lc.deinit(); |
| 58 | + var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator); |
| 59 | + defer args.deinit(); |
| 60 | + _ = args.next(); |
| 61 | + var config_path: ?[]const u8 = null; |
| 62 | + while (args.next()) |a| { |
| 63 | + const arg = std.mem.sliceTo(a, 0); |
| 64 | + if (std.mem.eql(u8, arg, "--config")) { |
| 65 | + if (args.next()) |v| config_path = std.mem.sliceTo(v, 0); |
| 66 | + } |
| 67 | + } |
| 68 | + var cfg: swerver.config.ServerConfig = blk: { |
| 69 | + if (config_path) |p| { |
| 70 | + loaded = try swerver.config_file.loadConfigFile(allocator, p); |
| 71 | + break :blk loaded.?.server_config; |
| 72 | + } |
| 73 | + break :blk swerver.config.ServerConfig.default(); |
| 74 | + }; |
| 75 | + try cfg.validate(); |
| 76 | + |
| 77 | + var app = router.Router.init(.{}); |
| 78 | + try app.get("/", handleRoot); |
| 79 | + try app.post("/", handleRoot); |
| 80 | + try app.get("/echo", handleEcho); |
| 81 | + try app.post("/echo", handleEcho); |
| 82 | + try app.get("/cookie", handleCookie); |
| 83 | + |
| 84 | + const srv = try swerver.ServerBuilder.config(cfg).router(app).disablePreencoded().build(allocator); |
| 85 | + defer { srv.deinit(); allocator.destroy(srv); } |
| 86 | + try srv.run(null); |
| 87 | +} |
0 commit comments