diff --git a/src/request.zig b/src/request.zig index 8ddb904..2f21111 100644 --- a/src/request.zig +++ b/src/request.zig @@ -354,12 +354,12 @@ pub fn _internal_sendError(self: *const Request, err: anyerror, err_trace: ?std. // TODO: let's hope 20k is enough. Maybe just really allocate here self.h.*.status = errorcode_num; var buf: [20 * 1024]u8 = undefined; - var writer = std.io.Writer.fixed(&buf); + var writer = std.Io.Writer.fixed(&buf); try writer.print("ERROR: {any}\n\n", .{err}); if (err_trace) |trace| { const debugInfo = try std.debug.getSelfDebugInfo(); - const ttyConfig: std.io.tty.Config = .no_color; + const ttyConfig: std.Io.tty.Config = .no_color; try std.debug.writeStackTrace(trace, &writer, debugInfo, ttyConfig); } diff --git a/src/tests/test_sendfile.zig b/src/tests/test_sendfile.zig index f13cbdd..76f6273 100644 --- a/src/tests/test_sendfile.zig +++ b/src/tests/test_sendfile.zig @@ -18,7 +18,7 @@ fn makeRequest(a: std.mem.Allocator, url: []const u8) !void { var http_client: std.http.Client = .{ .allocator = a }; defer http_client.deinit(); - var response_writer = std.io.Writer.Allocating.init(a); + var response_writer = std.Io.Writer.Allocating.init(a); defer response_writer.deinit(); _ = try http_client.fetch(.{ diff --git a/src/util.zig b/src/util.zig index 27e86d8..3c82a6d 100644 --- a/src/util.zig +++ b/src/util.zig @@ -77,7 +77,7 @@ pub fn stringifyBuf( value: anytype, options: std.json.Stringify.Options, ) ![]const u8 { - var w: std.io.Writer = .fixed(buffer); + var w: std.Io.Writer = .fixed(buffer); if (std.json.Stringify.value(value, options, &w)) { return w.buffered(); } else |err| { // error diff --git a/src/zap.zig b/src/zap.zig index 4e20ddd..bf68630 100644 --- a/src/zap.zig +++ b/src/zap.zig @@ -140,8 +140,8 @@ pub const HttpListenerSettings = struct { on_response: ?HttpRequestFn = null, on_upgrade: ?HttpUpgradeFn = null, on_finish: ?HttpFinishFn = null, - // provide any pointer in there for "user data". it will be passed pack in - // on_finish()'s copy of the struct_http_settings_s + /// User defined data can be extracted in a request handler using `Request.getUserContext(T)` + /// In `on_finish()` it can be extracted using `HttpListener.getUserContext(T, fio.struct_http_settings_s.udata.?)` udata: ?*anyopaque = null, public_folder: ?[]const u8 = null, max_clients: ?isize = null, @@ -156,99 +156,101 @@ pub const HttpListenerSettings = struct { /// Http listener pub const HttpListener = struct { settings: HttpListenerSettings, + userData: ?*anyopaque, - var the_one_and_only_listener: ?*HttpListener = null; + const Self = @This(); /// Create a listener pub fn init(settings: HttpListenerSettings) HttpListener { std.debug.assert(settings.on_request != null); return .{ .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 pub fn theOneAndOnlyRequestCallBack(r: [*c]fio.http_s) callconv(.c) void { - if (the_one_and_only_listener) |l| { - var req: Request = .{ - .path = util.fio2str(r.*.path), - .query = util.fio2str(r.*.query), - .body = util.fio2str(r.*.body), - .method = util.fio2str(r.*.method), - .h = r, - ._is_finished_request_global = false, - ._user_context = undefined, + const self: *Self = @ptrCast(@alignCast(r.*.udata.?)); + var req: Request = .{ + .path = util.fio2str(r.*.path), + .query = util.fio2str(r.*.query), + .body = util.fio2str(r.*.body), + .method = util.fio2str(r.*.method), + .h = r, + ._is_finished_request_global = false, + ._user_context = undefined, + }; + req._is_finished = &req._is_finished_request_global; + + var user_context: Request.UserContext = .{ .user_context = self.userData }; + req._user_context = &user_context; + + req.markAsFinished(false); + std.debug.assert(self.settings.on_request != null); + if (self.settings.on_request) |on_request| { + on_request(req) catch |err| { + Logging.on_uncaught_error("HttpListener on_request", err); }; - req._is_finished = &req._is_finished_request_global; - - var user_context: Request.UserContext = .{}; - req._user_context = &user_context; - - req.markAsFinished(false); - std.debug.assert(l.settings.on_request != null); - if (l.settings.on_request) |on_request| { - on_request(req) catch |err| { - Logging.on_uncaught_error("HttpListener on_request", err); - }; - } } } /// Used internally: the listener's facilio response callback pub fn theOneAndOnlyResponseCallBack(r: [*c]fio.http_s) callconv(.c) void { - if (the_one_and_only_listener) |l| { - var req: Request = .{ - .path = util.fio2str(r.*.path), - .query = util.fio2str(r.*.query), - .body = util.fio2str(r.*.body), - .method = util.fio2str(r.*.method), - .h = r, - ._is_finished_request_global = false, - ._user_context = undefined, - }; - req._is_finished = &req._is_finished_request_global; + const self: *Self = @ptrCast(@alignCast(r.*.udata.?)); + var req: Request = .{ + .path = util.fio2str(r.*.path), + .query = util.fio2str(r.*.query), + .body = util.fio2str(r.*.body), + .method = util.fio2str(r.*.method), + .h = r, + ._is_finished_request_global = false, + ._user_context = undefined, + }; + req._is_finished = &req._is_finished_request_global; - var user_context: Request.UserContext = .{}; - req._user_context = &user_context; + var user_context: Request.UserContext = .{ .user_context = self.userData }; + req._user_context = &user_context; - l.settings.on_response.?(req) catch |err| { - Logging.on_uncaught_error("HttpListener on_response", err); - }; - } + self.settings.on_response.?(req) catch |err| { + Logging.on_uncaught_error("HttpListener on_response", err); + }; } /// 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 { - if (the_one_and_only_listener) |l| { - var req: Request = .{ - .path = util.fio2str(r.*.path), - .query = util.fio2str(r.*.query), - .body = util.fio2str(r.*.body), - .method = util.fio2str(r.*.method), - .h = r, - ._is_finished_request_global = false, - ._user_context = undefined, - }; - const zigtarget: []u8 = target[0..target_len]; - req._is_finished = &req._is_finished_request_global; + const self: *Self = @ptrCast(@alignCast(r.*.udata.?)); + var req: Request = .{ + .path = util.fio2str(r.*.path), + .query = util.fio2str(r.*.query), + .body = util.fio2str(r.*.body), + .method = util.fio2str(r.*.method), + .h = r, + ._is_finished_request_global = false, + ._user_context = undefined, + }; + const zigtarget: []u8 = target[0..target_len]; + req._is_finished = &req._is_finished_request_global; - var user_context: Request.UserContext = .{}; - req._user_context = &user_context; + var user_context: Request.UserContext = .{ .user_context = self.userData }; + req._user_context = &user_context; - l.settings.on_upgrade.?(req, zigtarget) catch |err| { - Logging.on_uncaught_error("HttpListener on_upgrade", err); - }; - } + self.settings.on_upgrade.?(req, zigtarget) catch |err| { + Logging.on_uncaught_error("HttpListener on_upgrade", err); + }; } /// Used internally: the listener's facilio finish callback pub fn theOneAndOnlyFinishCallBack(s: [*c]fio.struct_http_settings_s) callconv(.c) void { - if (the_one_and_only_listener) |l| { - l.settings.on_finish.?(s) catch |err| { - Logging.on_uncaught_error("HttpListener on_finish", err); - }; - } + const self: *Self = @ptrCast(@alignCast(s.*.udata.?)); + self.settings.on_finish.?(s) catch |err| { + Logging.on_uncaught_error("HttpListener on_finish", err); + }; } /// Start listening @@ -267,7 +269,7 @@ pub const HttpListener = struct { .on_upgrade = if (self.settings.on_upgrade) |_| HttpListener.theOneAndOnlyUpgradeCallBack else null, .on_response = if (self.settings.on_response) |_| HttpListener.theOneAndOnlyResponseCallBack else null, .on_finish = if (self.settings.on_finish) |_| HttpListener.theOneAndOnlyFinishCallBack else null, - .udata = null, + .udata = self, .public_folder = pfolder, .public_folder_length = pfolder_len, .max_header_size = 32 * 1024, @@ -297,14 +299,6 @@ pub const HttpListener = struct { if (ret == -1) { 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, keepalive_timeout_s: u8 = 5, log: bool = false, + udata: ?*anyopaque = null, /// Create settings with defaults pub fn init() ListenSettings { @@ -344,7 +339,7 @@ pub const LowLevel = struct { .on_upgrade = settings.on_upgrade, .on_response = settings.on_response, .on_finish = settings.on_finish, - .udata = null, + .udata = settings.udata, .public_folder = pfolder, .public_folder_length = pfolder_len, .max_header_size = settings.max_header_size,