mirror of
https://github.com/zigzap/zap.git
synced 2025-10-21 07:34:08 +00:00
progress: SimpleRequest, SimpleHttpListener, etc
This commit is contained in:
parent
e238a5d9ea
commit
f288d2418e
2 changed files with 167 additions and 35 deletions
|
@ -1,21 +1,30 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
fn on_request(r: [*c]zap.C.http_s) callconv(.C) void {
|
fn on_request(r: zap.SimpleRequest) void {
|
||||||
_ = zap.sendBody(r, "<html><body><h1>Hello from ZAP!!!</h1></body></html>");
|
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("<html><body><h1>Hello from ZAP!!!</h1></body></html>");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
// listen
|
var listener: zap.SimpleHttpListener = zap.SimpleHttpListener.init(.{
|
||||||
try zap.listen("3000", null, .{
|
.port = 3000,
|
||||||
.on_request = on_request,
|
.on_request = on_request,
|
||||||
.log = true,
|
.log = false,
|
||||||
});
|
});
|
||||||
|
try listener.listen();
|
||||||
|
|
||||||
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
|
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
|
||||||
|
|
||||||
// start working
|
// start worker threads
|
||||||
zap.start(.{
|
zap.start(.{
|
||||||
.threads = 4,
|
.threads = 2,
|
||||||
.workers = 4,
|
.workers = 2,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
177
src/deps/zap.zig
177
src/deps/zap.zig
|
@ -7,22 +7,156 @@ pub const C = @cImport({
|
||||||
@cInclude("fio.h");
|
@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 {
|
pub fn start(args: C.fio_start_args) void {
|
||||||
C.fio_start(args);
|
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{
|
const ListenError = error{
|
||||||
ValueNotZeroTerminated,
|
ValueNotZeroTerminated,
|
||||||
|
AlreadyListening,
|
||||||
ListenError,
|
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 {
|
pub fn listen(port: [*c]const u8, interface: [*c]const u8, settings: ListenSettings) ListenError!void {
|
||||||
var pfolder: [*c]const u8 = null;
|
var pfolder: [*c]const u8 = null;
|
||||||
var pfolder_len: usize = 0;
|
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,
|
.log = if (settings.log) 1 else 0,
|
||||||
.is_client = 0,
|
.is_client = 0,
|
||||||
};
|
};
|
||||||
// TODO: BUG: without this print statement, -Drelease* loop forever
|
// TODO: BUG: without this print/sleep statement, -Drelease* loop forever
|
||||||
// in debug2 and debug3
|
// in debug2 and debug3 of hello example
|
||||||
// std.debug.print("X\n", .{});
|
// std.debug.print("X\n", .{});
|
||||||
std.time.sleep(500 * 1000 * 1000);
|
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 {
|
// lower level sendBody
|
||||||
on_request: ?*const fn ([*c]C.http_s) callconv(.C) void = null,
|
pub fn sendBody(request: [*c]C.http_s, body: []const u8) void {
|
||||||
on_upgrade: ?*const fn ([*c]C.http_s, [*c]u8, usize) callconv(.C) void = null,
|
_ = C.http_send_body(request, @intToPtr(
|
||||||
on_response: ?*const fn ([*c]C.http_s) callconv(.C) void = null,
|
*anyopaque,
|
||||||
on_finish: ?*const fn ([*c]C.struct_http_settings_s) callconv(.C) void = null,
|
@ptrToInt(body.ptr),
|
||||||
public_folder: ?[]const u8 = null,
|
), body.len);
|
||||||
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 .{};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue