diff --git a/build.zig b/build.zig index 53132c7..f7d2b71 100644 --- a/build.zig +++ b/build.zig @@ -22,6 +22,7 @@ pub fn build(b: *std.build.Builder) !void { .{ .name = "hello2", .src = "examples/hello2/hello2.zig" }, .{ .name = "routes", .src = "examples/routes/routes.zig" }, .{ .name = "serve", .src = "examples/serve/serve.zig" }, + .{ .name = "hello_json", .src = "examples/hello_json/hello_json.zig" }, }) |excfg| { const ex_name = excfg.name; const ex_src = excfg.src; diff --git a/examples/hello_json/hello_json.zig b/examples/hello_json/hello_json.zig new file mode 100644 index 0000000..bbb0de3 --- /dev/null +++ b/examples/hello_json/hello_json.zig @@ -0,0 +1,74 @@ +const std = @import("std"); +const zap = @import("zap"); + +const User = struct { + first_name: ?[]const u8 = null, + last_name: ?[]const u8 = null, +}; + +fn on_request(r: zap.SimpleRequest) void { + if (!std.mem.eql(u8, r.method.?, "GET")) + return; + + // /user/n + if (r.path) |the_path| { + if (the_path.len < 7 or !std.mem.startsWith(u8, the_path, "/user/")) + return; + + const user_id: usize = @intCast(usize, the_path[6] - 0x30); + const user = users.get(user_id); + + var json_to_send: []const u8 = undefined; + if (JsonHelper.stringify(user, .{})) |json| { + json_to_send = json; + } else { + json_to_send = "null"; + } + std.debug.print("<< json: {s}\n", .{json_to_send}); + r.setContentType(.JSON); + _ = r.sendBody(json_to_send); + } +} + +const UserMap = std.AutoHashMap(usize, User); + +var users: UserMap = undefined; +fn setupUserData(a: std.mem.Allocator) !void { + users = UserMap.init(a); + try users.put(1, .{ .first_name = "renerocksai" }); + try users.put(2, .{ .first_name = "Your", .last_name = "Mom" }); +} + +const JsonHelper = struct { + // "static" instance of buffer + var buf: [100]u8 = undefined; + var fba = std.heap.FixedBufferAllocator.init(&buf); + + fn stringify(value: anytype, options: std.json.StringifyOptions) ?[]const u8 { + var string = std.ArrayList(u8).init(fba.allocator()); + if (std.json.stringify(value, options, string.writer())) { + return string.items; + } else |_| { // error + return null; + } + } +}; + +pub fn main() !void { + var a = std.heap.page_allocator; + try setupUserData(a); + var listener = zap.SimpleHttpListener.init(.{ + .port = 3000, + .on_request = on_request, + .log = false, + }); + try listener.listen(); + + std.debug.print("Listening on 0.0.0.0:3000\n", .{}); + + // start worker threads + zap.start(.{ + .threads = 2, + .workers = 2, + }); +} diff --git a/src/deps/zap.zig b/src/deps/zap.zig index 2535f92..79282af 100644 --- a/src/deps/zap.zig +++ b/src/deps/zap.zig @@ -7,16 +7,28 @@ pub const C = @cImport({ @cInclude("fio.h"); }); -pub fn start(args: C.fio_start_args) void { - C.fio_start(args); -} - pub fn fio2str(o: C.FIOBJ) ?[]const u8 { if (o == 0) return null; const x: C.fio_str_info_s = C.fiobj_obj2cstr(o); return std.mem.span(x.data); } +pub fn str2fio(s: []const u8) C.fio_str_info_s { + return .{ + .data = toCharPtr(s), + .len = s.len, + .capa = s.len, + }; +} + +fn toCharPtr(s: []const u8) [*c]u8 { + return @intToPtr([*c]u8, @ptrToInt(s.ptr)); +} + +pub fn start(args: C.fio_start_args) void { + C.fio_start(args); +} + const ListenError = error{ ValueNotZeroTerminated, AlreadyListening, @@ -28,6 +40,12 @@ const HttpParam = struct { value: []const u8, }; +const ContentType = enum { + TEXT, + HTML, + JSON, +}; + pub const SimpleRequest = struct { path: ?[]const u8, query: ?[]const u8, @@ -44,6 +62,32 @@ pub const SimpleRequest = struct { ), body.len); } + pub fn setContentType(self: *const Self, c: ContentType) void { + self.setHeader("content-type", switch (c) { + .TEXT => "text/plain", + .JSON => "application/json", + else => "text/html", + }); + } + + pub fn setHeader(self: *const Self, name: []const u8, value: []const u8) void { + const hname: C.fio_str_info_s = .{ + .data = toCharPtr(name), + .len = name.len, + .capa = name.len, + }; + const vname: C.fio_str_info_s = .{ + .data = toCharPtr(value), + .len = value.len, + .capa = value.len, + }; + _ = C.http_set_header2(self.h, hname, vname); + + // Note to self: + // const new_fiobj_str = C.fiobj_str_new(name.ptr, name.len); + // C.fiobj_free(new_fiobj_str); + } + pub fn nextParam(self: *const Self) ?HttpParam { if (self.h.*.params == 0) return null; var key: C.FIOBJ = undefined;