diff --git a/examples/hello/hello.zig b/examples/hello/hello.zig
index fd5dbc2..ee0f295 100644
--- a/examples/hello/hello.zig
+++ b/examples/hello/hello.zig
@@ -1,21 +1,30 @@
const std = @import("std");
const zap = @import("zap");
-fn on_request(r: [*c]zap.C.http_s) callconv(.C) void {
- _ = zap.sendBody(r, "
Hello from ZAP!!!
");
+fn on_request(r: zap.SimpleRequest) void {
+ if (r.path) |the_path| {
+ std.debug.print("PATH: {s}\n", .{the_path});
+ }
+
+ if (r.query) |the_query| {
+ std.debug.print("QUERY: {s}\n", .{the_query});
+ }
+ _ = r.sendBody("Hello from ZAP!!!
");
}
pub fn main() !void {
- // listen
- try zap.listen("3000", null, .{
+ var listener: zap.SimpleHttpListener = zap.SimpleHttpListener.init(.{
+ .port = 3000,
.on_request = on_request,
- .log = true,
+ .log = false,
});
+ try listener.listen();
+
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
- // start working
+ // start worker threads
zap.start(.{
- .threads = 4,
- .workers = 4,
+ .threads = 2,
+ .workers = 2,
});
}
diff --git a/src/deps/zap.zig b/src/deps/zap.zig
index 4464f98..d6d38dd 100644
--- a/src/deps/zap.zig
+++ b/src/deps/zap.zig
@@ -7,22 +7,156 @@ pub const C = @cImport({
@cInclude("fio.h");
});
-pub fn sendBody(request: [*c]C.http_s, body: []const u8) void {
- _ = C.http_send_body(request, @intToPtr(
- *anyopaque,
- @ptrToInt(body.ptr),
- ), body.len);
-}
-
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);
+}
+
const ListenError = error{
ValueNotZeroTerminated,
+ AlreadyListening,
ListenError,
};
+pub const SimpleRequest = struct {
+ path: ?[]const u8,
+ query: ?[]const u8,
+ h: [*c]C.http_s,
+
+ const Self = @This();
+
+ pub fn sendBody(self: *const Self, body: []const u8) c_int {
+ return C.http_send_body(self.h, @intToPtr(
+ *anyopaque,
+ @ptrToInt(body.ptr),
+ ), body.len);
+ }
+};
+
+pub const HttpRequestFn = *const fn (r: [*c]C.http_s) callconv(.C) void;
+pub const SimpleHttpRequestFn = *const fn (SimpleRequest) void;
+
+pub const SimpleHttpListenerSettings = struct {
+ port: usize,
+ interface: [*c]const u8 = null,
+ on_request: SimpleHttpRequestFn,
+ public_folder: ?[]u8 = null,
+ max_clients: ?u8 = null,
+ timeout: ?u8 = null,
+ log: bool = false,
+};
+
+pub const SimpleHttpListener = struct {
+ settings: SimpleHttpListenerSettings,
+
+ const Self = @This();
+ var the_one_and_only_listener: ?*SimpleHttpListener = null;
+
+ pub fn init(settings: SimpleHttpListenerSettings) Self {
+ return .{
+ .settings = settings,
+ };
+ }
+
+ // we could make it dynamic by passing a SimpleHttpListener via udata
+ pub fn theOneAndOnlyRequestCallBack(r: [*c]C.http_s) callconv(.C) void {
+ if (the_one_and_only_listener) |l| {
+ var req: SimpleRequest = .{
+ .path = fio2str(r.*.path),
+ .query = fio2str(r.*.query),
+ .h = r,
+ };
+ l.settings.on_request(req);
+ }
+ }
+
+ pub fn listen(self: *Self) !void {
+ var pfolder: [*c]const u8 = null;
+ var pfolder_len: usize = 0;
+
+ if (self.settings.public_folder) |pf| {
+ pfolder_len = pf.len;
+ // TODO: make sure it's 0-terminated!!!
+ if (pf[pf.len - 1] != 0) {
+ return error.ValueNotZeroTerminated;
+ }
+ pfolder = pf.ptr;
+ }
+
+ var x: C.http_settings_s = .{
+ .on_request = Self.theOneAndOnlyRequestCallBack,
+ .on_upgrade = null,
+ .on_response = null,
+ .on_finish = null,
+ .udata = null,
+ .public_folder = pfolder,
+ .public_folder_length = pfolder_len,
+ .max_header_size = 32 * 1024,
+ .max_body_size = 50 * 1024 * 1024,
+ .max_clients = self.settings.max_clients orelse 100,
+ .tls = null,
+ .reserved1 = 0,
+ .reserved2 = 0,
+ .reserved3 = 0,
+ .ws_max_msg_size = 0,
+ .timeout = self.settings.timeout orelse 5,
+ .ws_timeout = 0,
+ .log = if (self.settings.log) 1 else 0,
+ .is_client = 0,
+ };
+ // TODO: BUG: without this print/sleep statement, -Drelease* loop forever
+ // in debug2 and debug3 of hello example
+ // std.debug.print("X\n", .{});
+ std.time.sleep(500 * 1000 * 1000);
+
+ var portbuf: [100]u8 = undefined;
+ const printed_port = try std.fmt.bufPrintZ(&portbuf, "{d}", .{self.settings.port});
+
+ // pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![:0]u8 {
+ // const result = try bufPrint(buf, fmt ++ "\x00", args);
+ // return result[0 .. result.len - 1 :0];
+ // }
+ if (C.http_listen(printed_port.ptr, self.settings.interface, x) == -1) {
+ return error.ListenError;
+ }
+
+ // set ourselves up to handle requests:
+ // TODO: do we mind the race condition?
+ // the SimpleHttpRequestFn will check if this is null and not process
+ // the request if it isn't set. hence, if started under full load, the
+ // first request(s) might not be serviced, as long as it takes from
+ // C.http_listen() to here
+ Self.the_one_and_only_listener = self;
+ }
+};
+
+//
+// lower level listening
+//
+pub const ListenSettings = struct {
+ on_request: ?*const fn ([*c]C.http_s) callconv(.C) void = null,
+ on_upgrade: ?*const fn ([*c]C.http_s, [*c]u8, usize) callconv(.C) void = null,
+ on_response: ?*const fn ([*c]C.http_s) callconv(.C) void = null,
+ on_finish: ?*const fn ([*c]C.struct_http_settings_s) callconv(.C) void = null,
+ public_folder: ?[]const u8 = null,
+ max_header_size: usize = 32 * 1024,
+ max_body_size: usize = 50 * 1024 * 1024,
+ max_clients: isize = 100,
+ keepalive_timeout_s: u8 = 5,
+ log: bool = false,
+
+ const Self = @This();
+
+ pub fn init() Self {
+ return .{};
+ }
+};
+
pub fn listen(port: [*c]const u8, interface: [*c]const u8, settings: ListenSettings) ListenError!void {
var pfolder: [*c]const u8 = null;
var pfolder_len: usize = 0;
@@ -56,8 +190,8 @@ pub fn listen(port: [*c]const u8, interface: [*c]const u8, settings: ListenSetti
.log = if (settings.log) 1 else 0,
.is_client = 0,
};
- // TODO: BUG: without this print statement, -Drelease* loop forever
- // in debug2 and debug3
+ // TODO: BUG: without this print/sleep statement, -Drelease* loop forever
+ // in debug2 and debug3 of hello example
// std.debug.print("X\n", .{});
std.time.sleep(500 * 1000 * 1000);
@@ -66,21 +200,10 @@ pub fn listen(port: [*c]const u8, interface: [*c]const u8, settings: ListenSetti
}
}
-pub const ListenSettings = struct {
- on_request: ?*const fn ([*c]C.http_s) callconv(.C) void = null,
- on_upgrade: ?*const fn ([*c]C.http_s, [*c]u8, usize) callconv(.C) void = null,
- on_response: ?*const fn ([*c]C.http_s) callconv(.C) void = null,
- on_finish: ?*const fn ([*c]C.struct_http_settings_s) callconv(.C) void = null,
- public_folder: ?[]const u8 = null,
- max_header_size: usize = 32 * 1024,
- max_body_size: usize = 50 * 1024 * 1024,
- max_clients: isize = 100,
- keepalive_timeout_s: u8 = 5,
- log: bool = false,
-
- const Self = @This();
-
- pub fn init() Self {
- return .{};
- }
-};
+// lower level sendBody
+pub fn sendBody(request: [*c]C.http_s, body: []const u8) void {
+ _ = C.http_send_body(request, @intToPtr(
+ *anyopaque,
+ @ptrToInt(body.ptr),
+ ), body.len);
+}