1
0
Fork 0
mirror of https://github.com/zigzap/zap.git synced 2025-10-21 15:44:10 +00:00

Remove HttpListener's singleton constraint; Add userdata support to HttpListener; closes #181

This commit is contained in:
Tadej Gašparovič 2025-09-17 19:18:55 +02:00
parent de9f49e4a9
commit e8d789329a

View file

@ -140,8 +140,8 @@ pub const HttpListenerSettings = struct {
on_response: ?HttpRequestFn = null, on_response: ?HttpRequestFn = null,
on_upgrade: ?HttpUpgradeFn = null, on_upgrade: ?HttpUpgradeFn = null,
on_finish: ?HttpFinishFn = null, on_finish: ?HttpFinishFn = null,
// provide any pointer in there for "user data". it will be passed pack in /// User defined data can be extracted in a request handler using `Request.getUserContext(T)`
// on_finish()'s copy of the struct_http_settings_s /// In `on_finish()` it can be extracted using `HttpListener.getUserContext(T, fio.struct_http_settings_s.udata.?)`
udata: ?*anyopaque = null, udata: ?*anyopaque = null,
public_folder: ?[]const u8 = null, public_folder: ?[]const u8 = null,
max_clients: ?isize = null, max_clients: ?isize = null,
@ -156,21 +156,27 @@ pub const HttpListenerSettings = struct {
/// Http listener /// Http listener
pub const HttpListener = struct { pub const HttpListener = struct {
settings: HttpListenerSettings, settings: HttpListenerSettings,
userData: ?*anyopaque,
var the_one_and_only_listener: ?*HttpListener = null; const Self = @This();
/// Create a listener /// Create a listener
pub fn init(settings: HttpListenerSettings) HttpListener { pub fn init(settings: HttpListenerSettings) HttpListener {
std.debug.assert(settings.on_request != null); std.debug.assert(settings.on_request != null);
return .{ return .{
.settings = settings, .settings = settings,
.userData = settings.udata,
}; };
} }
// we could make it dynamic by passing a HttpListener via udata pub fn getUserContext(comptime T: type, udata: *anyopaque) ?*T {
const self: *Self = @ptrCast(@alignCast(udata));
return @ptrCast(@alignCast(self.userData));
}
/// Used internally: the listener's facilio request callback /// Used internally: the listener's facilio request callback
pub fn theOneAndOnlyRequestCallBack(r: [*c]fio.http_s) callconv(.c) void { pub fn theOneAndOnlyRequestCallBack(r: [*c]fio.http_s) callconv(.c) void {
if (the_one_and_only_listener) |l| { const self: *Self = @ptrCast(@alignCast(r.*.udata.?));
var req: Request = .{ var req: Request = .{
.path = util.fio2str(r.*.path), .path = util.fio2str(r.*.path),
.query = util.fio2str(r.*.query), .query = util.fio2str(r.*.query),
@ -182,22 +188,21 @@ pub const HttpListener = struct {
}; };
req._is_finished = &req._is_finished_request_global; req._is_finished = &req._is_finished_request_global;
var user_context: Request.UserContext = .{}; var user_context: Request.UserContext = .{ .user_context = self.userData };
req._user_context = &user_context; req._user_context = &user_context;
req.markAsFinished(false); req.markAsFinished(false);
std.debug.assert(l.settings.on_request != null); std.debug.assert(self.settings.on_request != null);
if (l.settings.on_request) |on_request| { if (self.settings.on_request) |on_request| {
on_request(req) catch |err| { on_request(req) catch |err| {
Logging.on_uncaught_error("HttpListener on_request", err); Logging.on_uncaught_error("HttpListener on_request", err);
}; };
} }
} }
}
/// Used internally: the listener's facilio response callback /// Used internally: the listener's facilio response callback
pub fn theOneAndOnlyResponseCallBack(r: [*c]fio.http_s) callconv(.c) void { pub fn theOneAndOnlyResponseCallBack(r: [*c]fio.http_s) callconv(.c) void {
if (the_one_and_only_listener) |l| { const self: *Self = @ptrCast(@alignCast(r.*.udata.?));
var req: Request = .{ var req: Request = .{
.path = util.fio2str(r.*.path), .path = util.fio2str(r.*.path),
.query = util.fio2str(r.*.query), .query = util.fio2str(r.*.query),
@ -209,18 +214,17 @@ pub const HttpListener = struct {
}; };
req._is_finished = &req._is_finished_request_global; req._is_finished = &req._is_finished_request_global;
var user_context: Request.UserContext = .{}; var user_context: Request.UserContext = .{ .user_context = self.userData };
req._user_context = &user_context; req._user_context = &user_context;
l.settings.on_response.?(req) catch |err| { self.settings.on_response.?(req) catch |err| {
Logging.on_uncaught_error("HttpListener on_response", err); Logging.on_uncaught_error("HttpListener on_response", err);
}; };
} }
}
/// Used internally: the listener's facilio upgrade callback /// Used internally: the listener's facilio upgrade callback
pub fn theOneAndOnlyUpgradeCallBack(r: [*c]fio.http_s, target: [*c]u8, target_len: usize) callconv(.c) void { pub fn theOneAndOnlyUpgradeCallBack(r: [*c]fio.http_s, target: [*c]u8, target_len: usize) callconv(.c) void {
if (the_one_and_only_listener) |l| { const self: *Self = @ptrCast(@alignCast(r.*.udata.?));
var req: Request = .{ var req: Request = .{
.path = util.fio2str(r.*.path), .path = util.fio2str(r.*.path),
.query = util.fio2str(r.*.query), .query = util.fio2str(r.*.query),
@ -233,23 +237,21 @@ pub const HttpListener = struct {
const zigtarget: []u8 = target[0..target_len]; const zigtarget: []u8 = target[0..target_len];
req._is_finished = &req._is_finished_request_global; req._is_finished = &req._is_finished_request_global;
var user_context: Request.UserContext = .{}; var user_context: Request.UserContext = .{ .user_context = self.userData };
req._user_context = &user_context; req._user_context = &user_context;
l.settings.on_upgrade.?(req, zigtarget) catch |err| { self.settings.on_upgrade.?(req, zigtarget) catch |err| {
Logging.on_uncaught_error("HttpListener on_upgrade", err); Logging.on_uncaught_error("HttpListener on_upgrade", err);
}; };
} }
}
/// Used internally: the listener's facilio finish callback /// Used internally: the listener's facilio finish callback
pub fn theOneAndOnlyFinishCallBack(s: [*c]fio.struct_http_settings_s) callconv(.c) void { pub fn theOneAndOnlyFinishCallBack(s: [*c]fio.struct_http_settings_s) callconv(.c) void {
if (the_one_and_only_listener) |l| { const self: *Self = @ptrCast(@alignCast(s.*.udata.?));
l.settings.on_finish.?(s) catch |err| { self.settings.on_finish.?(s) catch |err| {
Logging.on_uncaught_error("HttpListener on_finish", err); Logging.on_uncaught_error("HttpListener on_finish", err);
}; };
} }
}
/// Start listening /// Start listening
pub fn listen(self: *HttpListener) !void { pub fn listen(self: *HttpListener) !void {
@ -267,7 +269,7 @@ pub const HttpListener = struct {
.on_upgrade = if (self.settings.on_upgrade) |_| HttpListener.theOneAndOnlyUpgradeCallBack else null, .on_upgrade = if (self.settings.on_upgrade) |_| HttpListener.theOneAndOnlyUpgradeCallBack else null,
.on_response = if (self.settings.on_response) |_| HttpListener.theOneAndOnlyResponseCallBack else null, .on_response = if (self.settings.on_response) |_| HttpListener.theOneAndOnlyResponseCallBack else null,
.on_finish = if (self.settings.on_finish) |_| HttpListener.theOneAndOnlyFinishCallBack else null, .on_finish = if (self.settings.on_finish) |_| HttpListener.theOneAndOnlyFinishCallBack else null,
.udata = null, .udata = self,
.public_folder = pfolder, .public_folder = pfolder,
.public_folder_length = pfolder_len, .public_folder_length = pfolder_len,
.max_header_size = 32 * 1024, .max_header_size = 32 * 1024,
@ -297,14 +299,6 @@ pub const HttpListener = struct {
if (ret == -1) { if (ret == -1) {
return error.ListenError; return error.ListenError;
} }
// set ourselves up to handle requests:
// TODO: do we mind the race condition?
// the HttpRequestFn 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
// fio.http_listen() to here
HttpListener.the_one_and_only_listener = self;
} }
}; };
@ -323,6 +317,7 @@ pub const LowLevel = struct {
max_clients: isize = 100, max_clients: isize = 100,
keepalive_timeout_s: u8 = 5, keepalive_timeout_s: u8 = 5,
log: bool = false, log: bool = false,
udata: ?*anyopaque = null,
/// Create settings with defaults /// Create settings with defaults
pub fn init() ListenSettings { pub fn init() ListenSettings {
@ -344,7 +339,7 @@ pub const LowLevel = struct {
.on_upgrade = settings.on_upgrade, .on_upgrade = settings.on_upgrade,
.on_response = settings.on_response, .on_response = settings.on_response,
.on_finish = settings.on_finish, .on_finish = settings.on_finish,
.udata = null, .udata = settings.udata,
.public_folder = pfolder, .public_folder = pfolder,
.public_folder_length = pfolder_len, .public_folder_length = pfolder_len,
.max_header_size = settings.max_header_size, .max_header_size = settings.max_header_size,