From fcce4517def8245d809d495cc9150a0abcb57da1 Mon Sep 17 00:00:00 2001 From: renerocksai Date: Sun, 16 Mar 2025 20:16:14 +0100 Subject: [PATCH] introduced error union to request fn return type --- examples/accept/accept.zig | 20 +-- examples/bindataformpost/bindataformpost.zig | 6 +- examples/cookies/cookies.zig | 2 +- examples/endpoint/main.zig | 4 +- examples/endpoint/stopendpoint.zig | 13 +- examples/endpoint/userweb.zig | 53 ++++---- examples/endpoint_auth/endpoint_auth.zig | 15 +- examples/hello/hello.zig | 8 +- examples/hello2/hello2.zig | 8 +- examples/hello_json/hello_json.zig | 16 +-- examples/http_params/http_params.zig | 6 +- examples/https/https.zig | 8 +- examples/middleware/middleware.zig | 6 +- .../middleware_with_endpoint.zig | 20 +-- examples/mustache/mustache.zig | 2 +- examples/routes/routes.zig | 20 +-- examples/senderror/senderror.zig | 2 +- examples/sendfile/sendfile.zig | 2 +- examples/serve/serve.zig | 2 +- examples/simple_router/simple_router.zig | 12 +- .../userpass_session_auth.zig | 22 +-- examples/websockets/websockets.zig | 6 +- src/endpoint.zig | 128 +++++++++++------- src/middleware.zig | 24 ++-- src/router.zig | 14 +- src/tests/test_auth.zig | 15 +- src/tests/test_http_params.zig | 2 +- src/tests/test_sendfile.zig | 2 +- src/util.zig | 6 +- src/zap.zig | 13 +- tools/docserver.zig | 2 +- wrk/zig/main.zig | 4 +- 32 files changed, 246 insertions(+), 217 deletions(-) diff --git a/examples/accept/accept.zig b/examples/accept/accept.zig index 55c9b53..a9e268c 100644 --- a/examples/accept/accept.zig +++ b/examples/accept/accept.zig @@ -5,7 +5,7 @@ var gpa = std.heap.GeneralPurposeAllocator(.{ .thread_safe = true, }){}; -fn on_request_verbose(r: zap.Request) void { +fn on_request_verbose(r: zap.Request) !void { // use a local buffer for the parsed accept headers var accept_buffer: [1024]u8 = undefined; var fba = std.heap.FixedBufferAllocator.init(&accept_buffer); @@ -21,38 +21,38 @@ fn on_request_verbose(r: zap.Request) void { break :content_type .HTML; }; - r.setContentType(content_type) catch return; + try r.setContentType(content_type); switch (content_type) { .TEXT => { - r.sendBody("Hello from ZAP!!!") catch return; + try r.sendBody("Hello from ZAP!!!"); }, .HTML => { - r.sendBody("

Hello from ZAP!!!

") catch return; + try r.sendBody("

Hello from ZAP!!!

"); }, .XML => { - r.sendBody( + try r.sendBody( \\ \\ \\ \\ Hello from ZAP!!! \\ \\ - ) catch return; + ); }, .JSON => { var buffer: [128]u8 = undefined; - const json = zap.util.stringifyBuf(&buffer, .{ .message = "Hello from ZAP!!!" }, .{}) orelse return; - r.sendJson(json) catch return; + const json = try zap.util.stringifyBuf(&buffer, .{ .message = "Hello from ZAP!!!" }, .{}); + try r.sendJson(json); }, .XHTML => { - r.sendBody( + try r.sendBody( \\ \\ \\ \\

Hello from ZAP!!!

\\ \\ - ) catch return; + ); }, } } diff --git a/examples/bindataformpost/bindataformpost.zig b/examples/bindataformpost/bindataformpost.zig index 4c2c7ad..9cc0eda 100644 --- a/examples/bindataformpost/bindataformpost.zig +++ b/examples/bindataformpost/bindataformpost.zig @@ -4,7 +4,7 @@ const zap = @import("zap"); const Handler = struct { var alloc: std.mem.Allocator = undefined; - pub fn on_request(r: zap.Request) void { + pub fn on_request(r: zap.Request) !void { // parse for FORM (body) parameters first r.parseBody() catch |err| { std.log.err("Parse Body error: {any}. Expected if body is empty", .{err}); @@ -24,7 +24,7 @@ const Handler = struct { // // HERE WE HANDLE THE BINARY FILE // - const params = r.parametersToOwnedList(Handler.alloc, false) catch unreachable; + const params = try r.parametersToOwnedList(Handler.alloc, false); defer params.deinit(); for (params.items) |kv| { if (kv.value) |v| { @@ -82,7 +82,7 @@ const Handler = struct { } else |err| { std.log.err("cannot check for terminate param: {any}\n", .{err}); } - r.sendJson("{ \"ok\": true }") catch unreachable; + try r.sendJson("{ \"ok\": true }"); } }; diff --git a/examples/cookies/cookies.zig b/examples/cookies/cookies.zig index 087731f..16fc9a8 100644 --- a/examples/cookies/cookies.zig +++ b/examples/cookies/cookies.zig @@ -35,7 +35,7 @@ pub fn main() !void { const Handler = struct { var alloc: std.mem.Allocator = undefined; - pub fn on_request(r: zap.Request) void { + pub fn on_request(r: zap.Request) !void { std.debug.print("\n=====================================================\n", .{}); defer std.debug.print("=====================================================\n\n", .{}); diff --git a/examples/endpoint/main.zig b/examples/endpoint/main.zig index d6b519b..a38803a 100644 --- a/examples/endpoint/main.zig +++ b/examples/endpoint/main.zig @@ -4,12 +4,12 @@ const UserWeb = @import("userweb.zig"); const StopEndpoint = @import("stopendpoint.zig"); // this is just to demo that we can catch arbitrary slugs as fallback -fn on_request(r: zap.Request) void { +fn on_request(r: zap.Request) !void { if (r.path) |the_path| { std.debug.print("REQUESTED PATH: {s}\n", .{the_path}); } - r.sendBody("

Hello from ZAP!!!

") catch return; + try r.sendBody("

Hello from ZAP!!!

"); } pub fn main() !void { diff --git a/examples/endpoint/stopendpoint.zig b/examples/endpoint/stopendpoint.zig index b9a7c46..d0e0a12 100644 --- a/examples/endpoint/stopendpoint.zig +++ b/examples/endpoint/stopendpoint.zig @@ -6,6 +6,7 @@ const zap = @import("zap"); pub const Self = @This(); path: []const u8, +error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response, pub fn init(path: []const u8) Self { return .{ @@ -13,14 +14,14 @@ pub fn init(path: []const u8) Self { }; } -pub fn get(e: *Self, r: zap.Request) void { +pub fn get(e: *Self, r: zap.Request) anyerror!void { _ = e; _ = r; zap.stop(); } -pub fn post(_: *Self, _: zap.Request) void {} -pub fn put(_: *Self, _: zap.Request) void {} -pub fn delete(_: *Self, _: zap.Request) void {} -pub fn patch(_: *Self, _: zap.Request) void {} -pub fn options(_: *Self, _: zap.Request) void {} +pub fn post(_: *Self, _: zap.Request) anyerror!void {} +pub fn put(_: *Self, _: zap.Request) anyerror!void {} +pub fn delete(_: *Self, _: zap.Request) anyerror!void {} +pub fn patch(_: *Self, _: zap.Request) anyerror!void {} +pub fn options(_: *Self, _: zap.Request) anyerror!void {} diff --git a/examples/endpoint/userweb.zig b/examples/endpoint/userweb.zig index 31cc587..e492408 100644 --- a/examples/endpoint/userweb.zig +++ b/examples/endpoint/userweb.zig @@ -11,6 +11,7 @@ alloc: std.mem.Allocator = undefined, _users: Users = undefined, path: []const u8, +error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response, pub fn init( a: std.mem.Allocator, @@ -42,8 +43,8 @@ fn userIdFromPath(self: *Self, path: []const u8) ?usize { return null; } -pub fn put(_: *Self, _: zap.Request) void {} -pub fn get(self: *Self, r: zap.Request) void { +pub fn put(_: *Self, _: zap.Request) anyerror!void {} +pub fn get(self: *Self, r: zap.Request) anyerror!void { if (r.path) |path| { // /users if (path.len == self.path.len) { @@ -52,33 +53,31 @@ pub fn get(self: *Self, r: zap.Request) void { var jsonbuf: [256]u8 = undefined; if (self.userIdFromPath(path)) |id| { if (self._users.get(id)) |user| { - if (zap.util.stringifyBuf(&jsonbuf, user, .{})) |json| { - r.sendJson(json) catch return; - } + const json = try zap.util.stringifyBuf(&jsonbuf, user, .{}); + try r.sendJson(json); } } } } -fn listUsers(self: *Self, r: zap.Request) void { +fn listUsers(self: *Self, r: zap.Request) !void { if (self._users.toJSON()) |json| { defer self.alloc.free(json); - r.sendJson(json) catch return; + try r.sendJson(json); } else |err| { - std.debug.print("LIST error: {}\n", .{err}); + return err; } } -pub fn post(self: *Self, r: zap.Request) void { +pub fn post(self: *Self, r: zap.Request) anyerror!void { if (r.body) |body| { const maybe_user: ?std.json.Parsed(User) = std.json.parseFromSlice(User, self.alloc, body, .{}) catch null; if (maybe_user) |u| { defer u.deinit(); if (self._users.addByName(u.value.first_name, u.value.last_name)) |id| { var jsonbuf: [128]u8 = undefined; - if (zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| { - r.sendJson(json) catch return; - } + const json = try zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{}); + try r.sendJson(json); } else |err| { std.debug.print("ADDING error: {}\n", .{err}); return; @@ -87,7 +86,7 @@ pub fn post(self: *Self, r: zap.Request) void { } } -pub fn patch(self: *Self, r: zap.Request) void { +pub fn patch(self: *Self, r: zap.Request) anyerror!void { if (r.path) |path| { if (self.userIdFromPath(path)) |id| { if (self._users.get(id)) |_| { @@ -97,13 +96,11 @@ pub fn patch(self: *Self, r: zap.Request) void { defer u.deinit(); var jsonbuf: [128]u8 = undefined; if (self._users.update(id, u.value.first_name, u.value.last_name)) { - if (zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| { - r.sendJson(json) catch return; - } + const json = try zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{}); + try r.sendJson(json); } else { - if (zap.util.stringifyBuf(&jsonbuf, .{ .status = "ERROR", .id = id }, .{})) |json| { - r.sendJson(json) catch return; - } + const json = try zap.util.stringifyBuf(&jsonbuf, .{ .status = "ERROR", .id = id }, .{}); + try r.sendJson(json); } } } @@ -112,26 +109,24 @@ pub fn patch(self: *Self, r: zap.Request) void { } } -pub fn delete(self: *Self, r: zap.Request) void { +pub fn delete(self: *Self, r: zap.Request) anyerror!void { if (r.path) |path| { if (self.userIdFromPath(path)) |id| { var jsonbuf: [128]u8 = undefined; if (self._users.delete(id)) { - if (zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| { - r.sendJson(json) catch return; - } + const json = try zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{}); + try r.sendJson(json); } else { - if (zap.util.stringifyBuf(&jsonbuf, .{ .status = "ERROR", .id = id }, .{})) |json| { - r.sendJson(json) catch return; - } + const json = try zap.util.stringifyBuf(&jsonbuf, .{ .status = "ERROR", .id = id }, .{}); + try r.sendJson(json); } } } } -pub fn options(_: *Self, r: zap.Request) void { - r.setHeader("Access-Control-Allow-Origin", "*") catch return; - r.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") catch return; +pub fn options(_: *Self, r: zap.Request) anyerror!void { + try r.setHeader("Access-Control-Allow-Origin", "*"); + try r.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS"); r.setStatus(zap.http.StatusCode.no_content); r.markAsFinished(true); } diff --git a/examples/endpoint_auth/endpoint_auth.zig b/examples/endpoint_auth/endpoint_auth.zig index f6f1e8a..57b2a37 100644 --- a/examples/endpoint_auth/endpoint_auth.zig +++ b/examples/endpoint_auth/endpoint_auth.zig @@ -13,24 +13,25 @@ const HTTP_RESPONSE: []const u8 = const Endpoint = struct { // the slug path: []const u8, + error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response, // authenticated requests go here - pub fn get(_: *Endpoint, r: zap.Request) void { + pub fn get(_: *Endpoint, r: zap.Request) !void { r.sendBody(HTTP_RESPONSE) catch return; } // just for fun, we also catch the unauthorized callback - pub fn unauthorized(_: *Endpoint, r: zap.Request) void { + pub fn unauthorized(_: *Endpoint, r: zap.Request) !void { r.setStatus(.unauthorized); r.sendBody("UNAUTHORIZED ACCESS") catch return; } // not implemented, don't care - pub fn post(_: *Endpoint, _: zap.Request) void {} - pub fn put(_: *Endpoint, _: zap.Request) void {} - pub fn delete(_: *Endpoint, _: zap.Request) void {} - pub fn patch(_: *Endpoint, _: zap.Request) void {} - pub fn options(_: *Endpoint, _: zap.Request) void {} + pub fn post(_: *Endpoint, _: zap.Request) !void {} + pub fn put(_: *Endpoint, _: zap.Request) !void {} + pub fn delete(_: *Endpoint, _: zap.Request) !void {} + pub fn patch(_: *Endpoint, _: zap.Request) !void {} + pub fn options(_: *Endpoint, _: zap.Request) !void {} }; pub fn main() !void { diff --git a/examples/hello/hello.zig b/examples/hello/hello.zig index 8b0f760..c7db536 100644 --- a/examples/hello/hello.zig +++ b/examples/hello/hello.zig @@ -1,7 +1,7 @@ const std = @import("std"); const zap = @import("zap"); -fn on_request_verbose(r: zap.Request) void { +fn on_request_verbose(r: zap.Request) !void { if (r.path) |the_path| { std.debug.print("PATH: {s}\n", .{the_path}); } @@ -9,11 +9,11 @@ fn on_request_verbose(r: zap.Request) void { if (r.query) |the_query| { std.debug.print("QUERY: {s}\n", .{the_query}); } - r.sendBody("

Hello from ZAP!!!

") catch return; + try r.sendBody("

Hello from ZAP!!!

"); } -fn on_request_minimal(r: zap.Request) void { - r.sendBody("

Hello from ZAP!!!

") catch return; +fn on_request_minimal(r: zap.Request) !void { + try r.sendBody("

Hello from ZAP!!!

"); } pub fn main() !void { diff --git a/examples/hello2/hello2.zig b/examples/hello2/hello2.zig index 4f43a5e..51e8faa 100644 --- a/examples/hello2/hello2.zig +++ b/examples/hello2/hello2.zig @@ -1,7 +1,7 @@ const std = @import("std"); const zap = @import("zap"); -fn on_request(r: zap.Request) void { +fn on_request(r: zap.Request) !void { const m = r.methodAsEnum(); const m_str = r.method orelse ""; const p = r.path orelse "/"; @@ -20,8 +20,8 @@ fn on_request(r: zap.Request) void { std.debug.print(">> BODY: {s}\n", .{the_body}); } - r.setContentTypeFromPath() catch return; - r.sendBody( + try r.setContentTypeFromPath(); + try r.sendBody( \\ \\

Hello from ZAP!!!

\\
@@ -32,7 +32,7 @@ fn on_request(r: zap.Request) void { \\ \\
\\ - ) catch return; + ); } pub fn main() !void { diff --git a/examples/hello_json/hello_json.zig b/examples/hello_json/hello_json.zig index 56c5cd8..21f9f46 100644 --- a/examples/hello_json/hello_json.zig +++ b/examples/hello_json/hello_json.zig @@ -6,7 +6,7 @@ const User = struct { last_name: ?[]const u8 = null, }; -fn on_request(r: zap.Request) void { +fn on_request(r: zap.Request) !void { if (r.methodAsEnum() != .GET) return; // /user/n @@ -14,16 +14,16 @@ fn on_request(r: zap.Request) void { if (the_path.len < 7 or !std.mem.startsWith(u8, the_path, "/user/")) return; - const user_id: usize = @as(usize, @intCast(the_path[6] - 0x30)); + const user_id: usize = @intCast(the_path[6] - 0x30); + std.debug.print("user_id: {d}\n", .{user_id}); + std.debug.print("users: {d}\n", .{users.count()}); const user = users.get(user_id); + std.debug.print("user: {?}\n", .{user}); - var buf: [100]u8 = undefined; + var buf: [256]u8 = undefined; var json_to_send: []const u8 = undefined; - if (zap.util.stringifyBuf(&buf, user, .{})) |json| { - json_to_send = json; - } else { - json_to_send = "null"; - } + json_to_send = try zap.util.stringifyBuf(&buf, user, .{}); + std.debug.print("<< json: {s}\n", .{json_to_send}); r.setContentType(.JSON) catch return; r.setContentTypeFromFilename("test.json") catch return; diff --git a/examples/http_params/http_params.zig b/examples/http_params/http_params.zig index e387e9b..66976f6 100644 --- a/examples/http_params/http_params.zig +++ b/examples/http_params/http_params.zig @@ -32,7 +32,7 @@ pub fn main() !void { const Handler = struct { var alloc: std.mem.Allocator = undefined; - pub fn on_request(r: zap.Request) void { + pub fn on_request(r: zap.Request) !void { std.debug.print("\n=====================================================\n", .{}); defer std.debug.print("=====================================================\n\n", .{}); @@ -69,7 +69,7 @@ pub fn main() !void { // ================================================================ // iterate over all params as strings - var strparams = r.parametersToOwnedStrList(alloc, false) catch unreachable; + var strparams = try r.parametersToOwnedStrList(alloc, false); defer strparams.deinit(); std.debug.print("\n", .{}); for (strparams.items) |kv| { @@ -79,7 +79,7 @@ pub fn main() !void { std.debug.print("\n", .{}); // iterate over all params - const params = r.parametersToOwnedList(alloc, false) catch unreachable; + const params = try r.parametersToOwnedList(alloc, false); defer params.deinit(); for (params.items) |kv| { std.log.info("Param `{s}` is {any}", .{ kv.key.str, kv.value }); diff --git a/examples/https/https.zig b/examples/https/https.zig index 2806179..bf9331b 100644 --- a/examples/https/https.zig +++ b/examples/https/https.zig @@ -1,7 +1,7 @@ const std = @import("std"); const zap = @import("zap"); -fn on_request_verbose(r: zap.Request) void { +fn on_request_verbose(r: zap.Request) !void { if (r.path) |the_path| { std.debug.print("PATH: {s}\n", .{the_path}); } @@ -9,11 +9,11 @@ fn on_request_verbose(r: zap.Request) void { if (r.query) |the_query| { std.debug.print("QUERY: {s}\n", .{the_query}); } - r.sendBody("

Hello from ZAP!!!

") catch return; + try r.sendBody("

Hello from ZAP!!!

"); } -fn on_request_minimal(r: zap.Request) void { - r.sendBody("

Hello from ZAP!!!

") catch return; +fn on_request_minimal(r: zap.Request) !void { + try r.sendBody("

Hello from ZAP!!!

"); } fn help_and_exit(filename: []const u8, err: anyerror) void { diff --git a/examples/middleware/middleware.zig b/examples/middleware/middleware.zig index 776a930..a02895f 100644 --- a/examples/middleware/middleware.zig +++ b/examples/middleware/middleware.zig @@ -69,7 +69,7 @@ const UserMiddleWare = struct { } // note that the first parameter is of type *Handler, not *Self !!! - pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) bool { + pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) !bool { // this is how we would get our self pointer const self: *Self = @fieldParentPtr("handler", handler); @@ -113,7 +113,7 @@ const SessionMiddleWare = struct { } // note that the first parameter is of type *Handler, not *Self !!! - pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) bool { + pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) !bool { // this is how we would get our self pointer const self: *Self = @fieldParentPtr("handler", handler); _ = self; @@ -148,7 +148,7 @@ const HtmlMiddleWare = struct { } // note that the first parameter is of type *Handler, not *Self !!! - pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) bool { + pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) !bool { // this is how we would get our self pointer const self: *Self = @fieldParentPtr("handler", handler); diff --git a/examples/middleware_with_endpoint/middleware_with_endpoint.zig b/examples/middleware_with_endpoint/middleware_with_endpoint.zig index 3bf9551..4d5f8b4 100644 --- a/examples/middleware_with_endpoint/middleware_with_endpoint.zig +++ b/examples/middleware_with_endpoint/middleware_with_endpoint.zig @@ -57,7 +57,7 @@ const UserMiddleWare = struct { } // note that the first parameter is of type *Handler, not *Self !!! - pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) bool { + pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) !bool { // this is how we would get our self pointer const self: *Self = @fieldParentPtr("handler", handler); @@ -103,7 +103,7 @@ const SessionMiddleWare = struct { } // note that the first parameter is of type *Handler, not *Self !!! - pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) bool { + pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) !bool { // this is how we would get our self pointer const self: *Self = @fieldParentPtr("handler", handler); _ = self; @@ -146,13 +146,13 @@ const HtmlEndpoint = struct { }; } - pub fn post(_: *HtmlEndpoint, _: zap.Request) void {} - pub fn put(_: *HtmlEndpoint, _: zap.Request) void {} - pub fn delete(_: *HtmlEndpoint, _: zap.Request) void {} - pub fn patch(_: *HtmlEndpoint, _: zap.Request) void {} - pub fn options(_: *HtmlEndpoint, _: zap.Request) void {} + pub fn post(_: *HtmlEndpoint, _: zap.Request) !void {} + pub fn put(_: *HtmlEndpoint, _: zap.Request) !void {} + pub fn delete(_: *HtmlEndpoint, _: zap.Request) !void {} + pub fn patch(_: *HtmlEndpoint, _: zap.Request) !void {} + pub fn options(_: *HtmlEndpoint, _: zap.Request) !void {} - pub fn get(_: *Self, r: zap.Request) void { + pub fn get(_: *Self, r: zap.Request) !void { var buf: [1024]u8 = undefined; var userFound: bool = false; var sessionFound: bool = false; @@ -169,12 +169,12 @@ const HtmlEndpoint = struct { sessionFound = true; std.debug.assert(r.isFinished() == false); - const message = std.fmt.bufPrint(&buf, "User: {s} / {s}, Session: {s} / {s}", .{ + const message = try std.fmt.bufPrint(&buf, "User: {s} / {s}, Session: {s} / {s}", .{ user.name, user.email, session.info, session.token, - }) catch unreachable; + }); r.setContentType(.TEXT) catch unreachable; r.sendBody(message) catch unreachable; std.debug.assert(r.isFinished() == true); diff --git a/examples/mustache/mustache.zig b/examples/mustache/mustache.zig index 26dd6d9..37ea7db 100644 --- a/examples/mustache/mustache.zig +++ b/examples/mustache/mustache.zig @@ -2,7 +2,7 @@ const std = @import("std"); const zap = @import("zap"); const Mustache = @import("zap").Mustache; -fn on_request(r: zap.Request) void { +fn on_request(r: zap.Request) !void { const template = \\ {{=<< >>=}} \\ * Users: diff --git a/examples/routes/routes.zig b/examples/routes/routes.zig index 43222d1..d605f3e 100644 --- a/examples/routes/routes.zig +++ b/examples/routes/routes.zig @@ -3,39 +3,39 @@ const zap = @import("zap"); // NOTE: this is a super simplified example, just using a hashmap to map // from HTTP path to request function. -fn dispatch_routes(r: zap.Request) void { +fn dispatch_routes(r: zap.Request) !void { // dispatch if (r.path) |the_path| { if (routes.get(the_path)) |foo| { - foo(r); + try foo(r); return; } } // or default: present menu - r.sendBody( + try r.sendBody( \\ \\ \\

static

\\

dynamic

\\ \\ - ) catch return; + ); } -fn static_site(r: zap.Request) void { - r.sendBody("

Hello from STATIC ZAP!

") catch return; +fn static_site(r: zap.Request) !void { + try r.sendBody("

Hello from STATIC ZAP!

"); } var dynamic_counter: i32 = 0; -fn dynamic_site(r: zap.Request) void { +fn dynamic_site(r: zap.Request) !void { dynamic_counter += 1; var buf: [128]u8 = undefined; - const filled_buf = std.fmt.bufPrintZ( + const filled_buf = try std.fmt.bufPrintZ( &buf, "

Hello # {d} from DYNAMIC ZAP!!!

", .{dynamic_counter}, - ) catch "ERROR"; - r.sendBody(filled_buf) catch return; + ); + try r.sendBody(filled_buf); } fn setup_routes(a: std.mem.Allocator) !void { diff --git a/examples/senderror/senderror.zig b/examples/senderror/senderror.zig index a9756ce..3fb0c81 100644 --- a/examples/senderror/senderror.zig +++ b/examples/senderror/senderror.zig @@ -5,7 +5,7 @@ fn MAKE_MEGA_ERROR() !void { return error.MEGA_ERROR; } -fn MY_REQUEST_HANDLER(r: zap.Request) void { +fn MY_REQUEST_HANDLER(r: zap.Request) !void { MAKE_MEGA_ERROR() catch |err| { r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505); }; diff --git a/examples/sendfile/sendfile.zig b/examples/sendfile/sendfile.zig index 4c97309..a4ac59e 100644 --- a/examples/sendfile/sendfile.zig +++ b/examples/sendfile/sendfile.zig @@ -6,7 +6,7 @@ var read_len: ?usize = null; const testfile = @embedFile("testfile.txt"); -pub fn on_request(r: zap.Request) void { +pub fn on_request(r: zap.Request) !void { // Sends a file if present in the filesystem orelse returns an error. // // - efficiently sends a file using gzip compression diff --git a/examples/serve/serve.zig b/examples/serve/serve.zig index d9f9d65..789257e 100644 --- a/examples/serve/serve.zig +++ b/examples/serve/serve.zig @@ -1,7 +1,7 @@ const std = @import("std"); const zap = @import("zap"); -fn on_request(r: zap.Request) void { +fn on_request(r: zap.Request) !void { r.setStatus(.not_found); r.sendBody("

404 - File not found

") catch return; } diff --git a/examples/simple_router/simple_router.zig b/examples/simple_router/simple_router.zig index eb58839..cacbcc3 100644 --- a/examples/simple_router/simple_router.zig +++ b/examples/simple_router/simple_router.zig @@ -2,7 +2,7 @@ const std = @import("std"); const zap = @import("zap"); const Allocator = std.mem.Allocator; -fn on_request_verbose(r: zap.Request) void { +fn on_request_verbose(r: zap.Request) !void { if (r.path) |the_path| { std.debug.print("PATH: {s}\n", .{the_path}); } @@ -28,7 +28,7 @@ pub const SomePackage = struct { }; } - pub fn getA(self: *Self, req: zap.Request) void { + pub fn getA(self: *Self, req: zap.Request) !void { std.log.warn("get_a_requested", .{}); const string = std.fmt.allocPrint( @@ -41,7 +41,7 @@ pub const SomePackage = struct { req.sendBody(string) catch return; } - pub fn getB(self: *Self, req: zap.Request) void { + pub fn getB(self: *Self, req: zap.Request) !void { std.log.warn("get_b_requested", .{}); const string = std.fmt.allocPrint( @@ -54,7 +54,7 @@ pub const SomePackage = struct { req.sendBody(string) catch return; } - pub fn incrementA(self: *Self, req: zap.Request) void { + pub fn incrementA(self: *Self, req: zap.Request) !void { std.log.warn("increment_a_requested", .{}); self.a += 1; @@ -63,10 +63,10 @@ pub const SomePackage = struct { } }; -fn not_found(req: zap.Request) void { +fn not_found(req: zap.Request) !void { std.debug.print("not found handler", .{}); - req.sendBody("Not found") catch return; + try req.sendBody("Not found"); } pub fn main() !void { diff --git a/examples/userpass_session_auth/userpass_session_auth.zig b/examples/userpass_session_auth/userpass_session_auth.zig index 550bfbb..c421ddc 100644 --- a/examples/userpass_session_auth/userpass_session_auth.zig +++ b/examples/userpass_session_auth/userpass_session_auth.zig @@ -25,38 +25,38 @@ const img = @embedFile("./html/Ziggy_the_Ziguana.svg.png"); var authenticator: Authenticator = undefined; // the login page (embedded) -fn on_login(r: zap.Request) void { - r.sendBody(loginpage) catch return; +fn on_login(r: zap.Request) !void { + try r.sendBody(loginpage); } // the "normal page" -fn on_normal_page(r: zap.Request) void { +fn on_normal_page(r: zap.Request) !void { zap.debug("on_normal_page()\n", .{}); - r.sendBody( + try r.sendBody( \\ \\

Hello from ZAP!!!

\\

You are logged in!!! \\

logout
\\ - ) catch return; + ); } // the logged-out page -fn on_logout(r: zap.Request) void { +fn on_logout(r: zap.Request) !void { zap.debug("on_logout()\n", .{}); authenticator.logout(&r); // note, the link below doesn't matter as the authenticator will send us // straight to the /login page - r.sendBody( + try r.sendBody( \\ \\

You are logged out!!!

\\
\\

Log back in

\\ - ) catch return; + ); } -fn on_request(r: zap.Request) void { +fn on_request(r: zap.Request) !void { switch (authenticator.authenticateRequest(&r)) { .Handled => { // the authenticator handled the entire request for us. @@ -80,8 +80,8 @@ fn on_request(r: zap.Request) void { // the authenticator. Hence, we name the img for the /login // page: /login/Ziggy....png if (std.mem.startsWith(u8, p, "/login/Ziggy_the_Ziguana.svg.png")) { - r.setContentTypeFromPath() catch unreachable; - r.sendBody(img) catch unreachable; + try r.setContentTypeFromPath(); + try r.sendBody(img); return; } diff --git a/examples/websockets/websockets.zig b/examples/websockets/websockets.zig index e347242..d0e3cee 100644 --- a/examples/websockets/websockets.zig +++ b/examples/websockets/websockets.zig @@ -163,13 +163,13 @@ fn handle_websocket_message( // // HTTP stuff // -fn on_request(r: zap.Request) void { +fn on_request(r: zap.Request) !void { r.setHeader("Server", "zap.example") catch unreachable; - r.sendBody( + try r.sendBody( \\ \\

This is a simple Websocket chatroom example

\\ - ) catch return; + ); } fn on_upgrade(r: zap.Request, target_protocol: []const u8) void { diff --git a/src/endpoint.zig b/src/endpoint.zig index d9b1b6e..6b7dd95 100644 --- a/src/endpoint.zig +++ b/src/endpoint.zig @@ -55,6 +55,16 @@ const std = @import("std"); const zap = @import("zap.zig"); const auth = @import("http_auth.zig"); +/// Endpoint request error handling strategy +pub const ErrorStrategy = enum { + /// log errors to console + log_to_console, + /// log errors to console AND generate a HTML response + log_to_response, + /// raise errors -> TODO: clarify: where can they be caught? in App.run() + raise, +}; + // zap types const Request = zap.Request; const ListenerSettings = zap.HttpListenerSettings; @@ -69,6 +79,14 @@ pub fn checkEndpointType(T: type) void { @compileError(@typeName(T) ++ " has no path field"); } + if (@hasField(T, "error_strategy")) { + if (@FieldType(T, "error_strategy") != ErrorStrategy) { + @compileError(@typeName(@FieldType(T, "error_strategy")) ++ " has wrong type, expected: zap.Endpoint.ErrorStrategy"); + } + } else { + @compileError(@typeName(T) ++ " has no error_strategy field"); + } + const methods_to_check = [_][]const u8{ "get", "post", @@ -79,8 +97,8 @@ pub fn checkEndpointType(T: type) void { }; inline for (methods_to_check) |method| { if (@hasDecl(T, method)) { - if (@TypeOf(@field(T, method)) != fn (_: *T, _: Request) void) { - @compileError(method ++ " method of " ++ @typeName(T) ++ " has wrong type:\n" ++ @typeName(@TypeOf(T.get)) ++ "\nexpected:\n" ++ @typeName(fn (_: *T, _: Request) void)); + if (@TypeOf(@field(T, method)) != fn (_: *T, _: Request) anyerror!void) { + @compileError(method ++ " method of " ++ @typeName(T) ++ " has wrong type:\n" ++ @typeName(@TypeOf(T.get)) ++ "\nexpected:\n" ++ @typeName(fn (_: *T, _: Request) anyerror!void)); } } else { @compileError(@typeName(T) ++ " has no method named `" ++ method ++ "`"); @@ -88,58 +106,65 @@ pub fn checkEndpointType(T: type) void { } } -const EndpointWrapper = struct { - pub const Wrapper = struct { - call: *const fn (*Wrapper, zap.Request) void = undefined, +pub const Wrapper = struct { + pub const Internal = struct { + call: *const fn (*Internal, zap.Request) anyerror!void = undefined, path: []const u8, - destroy: *const fn (allocator: std.mem.Allocator, *Wrapper) void = undefined, + destroy: *const fn (allocator: std.mem.Allocator, *Internal) void = undefined, }; pub fn Wrap(T: type) type { return struct { wrapped: *T, - wrapper: Wrapper, + wrapper: Internal, const Self = @This(); - pub fn unwrap(wrapper: *Wrapper) *Self { + pub fn unwrap(wrapper: *Internal) *Self { const self: *Self = @alignCast(@fieldParentPtr("wrapper", wrapper)); return self; } - pub fn destroy(allocator: std.mem.Allocator, wrapper: *Wrapper) void { + pub fn destroy(allocator: std.mem.Allocator, wrapper: *Internal) void { const self: *Self = @alignCast(@fieldParentPtr("wrapper", wrapper)); allocator.destroy(self); } - pub fn onRequestWrapped(wrapper: *Wrapper, r: zap.Request) void { + pub fn onRequestWrapped(wrapper: *Internal, r: zap.Request) !void { var self: *Self = Self.unwrap(wrapper); - self.onRequest(r); + try self.onRequest(r); } - pub fn onRequest(self: *Self, r: zap.Request) void { - switch (r.methodAsEnum()) { - .GET => return self.wrapped.*.get(r), - .POST => return self.wrapped.*.post(r), - .PUT => return self.wrapped.*.put(r), - .DELETE => return self.wrapped.*.delete(r), - .PATCH => return self.wrapped.*.patch(r), - .OPTIONS => return self.wrapped.*.options(r), - else => { - // TODO: log that method is ignored - }, + pub fn onRequest(self: *Self, r: zap.Request) !void { + const ret = switch (r.methodAsEnum()) { + .GET => self.wrapped.*.get(r), + .POST => self.wrapped.*.post(r), + .PUT => self.wrapped.*.put(r), + .DELETE => self.wrapped.*.delete(r), + .PATCH => self.wrapped.*.patch(r), + .OPTIONS => self.wrapped.*.options(r), + else => error.UnsupportedHtmlRequestMethod, + }; + if (ret) { + // handled without error + } else |err| { + switch (self.wrapped.*.error_strategy) { + .raise => return err, + .log_to_response => return r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505), + .log_to_console => zap.debug("Error in {} {s} : {}", .{ Self, r.method orelse "(no method)", err }), + } } } }; } - pub fn init(T: type, value: *T) EndpointWrapper.Wrap(T) { + pub fn init(T: type, value: *T) Wrapper.Wrap(T) { checkEndpointType(T); - var ret: EndpointWrapper.Wrap(T) = .{ + var ret: Wrapper.Wrap(T) = .{ .wrapped = value, .wrapper = .{ .path = value.path }, }; - ret.wrapper.call = EndpointWrapper.Wrap(T).onRequestWrapped; - ret.wrapper.destroy = EndpointWrapper.Wrap(T).destroy; + ret.wrapper.call = Wrapper.Wrap(T).onRequestWrapped; + ret.wrapper.destroy = Wrapper.Wrap(T).destroy; return ret; } }; @@ -150,6 +175,7 @@ pub fn Authenticating(EndpointType: type, Authenticator: type) type { authenticator: *Authenticator, ep: *EndpointType, path: []const u8, + error_strategy: ErrorStrategy, const Self = @This(); /// Init the authenticating endpoint. Pass in a pointer to the endpoint @@ -160,61 +186,62 @@ pub fn Authenticating(EndpointType: type, Authenticator: type) type { .authenticator = authenticator, .ep = e, .path = e.path, + .error_strategy = e.error_strategy, }; } /// Authenticates GET requests using the Authenticator. - pub fn get(self: *Self, r: zap.Request) void { - switch (self.authenticator.authenticateRequest(&r)) { + pub fn get(self: *Self, r: zap.Request) anyerror!void { + try switch (self.authenticator.authenticateRequest(&r)) { .AuthFailed => return self.ep.*.unauthorized(r), .AuthOK => self.ep.*.get(r), .Handled => {}, - } + }; } /// Authenticates POST requests using the Authenticator. - pub fn post(self: *Self, r: zap.Request) void { - switch (self.authenticator.authenticateRequest(&r)) { + pub fn post(self: *Self, r: zap.Request) anyerror!void { + try switch (self.authenticator.authenticateRequest(&r)) { .AuthFailed => return self.ep.*.unauthorized(r), .AuthOK => self.ep.*.post(r), .Handled => {}, - } + }; } /// Authenticates PUT requests using the Authenticator. - pub fn put(self: *Self, r: zap.Request) void { - switch (self.authenticator.authenticateRequest(&r)) { + pub fn put(self: *Self, r: zap.Request) anyerror!void { + try switch (self.authenticator.authenticateRequest(&r)) { .AuthFailed => return self.ep.*.unauthorized(r), .AuthOK => self.ep.*.put(r), .Handled => {}, - } + }; } /// Authenticates DELETE requests using the Authenticator. - pub fn delete(self: *Self, r: zap.Request) void { - switch (self.authenticator.authenticateRequest(&r)) { + pub fn delete(self: *Self, r: zap.Request) anyerror!void { + try switch (self.authenticator.authenticateRequest(&r)) { .AuthFailed => return self.ep.*.unauthorized(r), .AuthOK => self.ep.*.delete(r), .Handled => {}, - } + }; } /// Authenticates PATCH requests using the Authenticator. - pub fn patch(self: *Self, r: zap.Request) void { - switch (self.authenticator.authenticateRequest(&r)) { + pub fn patch(self: *Self, r: zap.Request) anyerror!void { + try switch (self.authenticator.authenticateRequest(&r)) { .AuthFailed => return self.ep.*.unauthorized(r), .AuthOK => self.ep.*.patch(r), .Handled => {}, - } + }; } /// Authenticates OPTIONS requests using the Authenticator. - pub fn options(self: *Self, r: zap.Request) void { - switch (self.authenticator.authenticateRequest(&r)) { + pub fn options(self: *Self, r: zap.Request) anyerror!void { + try switch (self.authenticator.authenticateRequest(&r)) { .AuthFailed => return self.ep.*.unauthorized(r), .AuthOK => self.ep.*.put(r), .Handled => {}, - } + }; } }; } @@ -237,7 +264,7 @@ pub const Listener = struct { const Self = @This(); /// Internal static struct of member endpoints - var endpoints: std.ArrayListUnmanaged(*EndpointWrapper.Wrapper) = .empty; + var endpoints: std.ArrayListUnmanaged(*Wrapper.Internal) = .empty; /// Internal, static request handler callback. Will be set to the optional, /// user-defined request callback that only gets called if no endpoints match @@ -303,23 +330,22 @@ pub const Listener = struct { } const EndpointType = @typeInfo(@TypeOf(e)).pointer.child; checkEndpointType(EndpointType); - const wrapper = try self.allocator.create(EndpointWrapper.Wrap(EndpointType)); - wrapper.* = EndpointWrapper.init(EndpointType, e); + const wrapper = try self.allocator.create(Wrapper.Wrap(EndpointType)); + wrapper.* = Wrapper.init(EndpointType, e); try endpoints.append(self.allocator, &wrapper.wrapper); } - fn onRequest(r: Request) void { + fn onRequest(r: Request) !void { if (r.path) |p| { for (endpoints.items) |wrapper| { if (std.mem.startsWith(u8, p, wrapper.path)) { - wrapper.call(wrapper, r); - return; + return try wrapper.call(wrapper, r); } } } // if set, call the user-provided default callback if (on_request) |foo| { - foo(r); + try foo(r); } } }; diff --git a/src/middleware.zig b/src/middleware.zig index 0373280..b5b92e2 100644 --- a/src/middleware.zig +++ b/src/middleware.zig @@ -17,7 +17,7 @@ pub fn Handler(comptime ContextType: anytype) type { // will be set allocator: ?std.mem.Allocator = null, - pub const RequestFn = *const fn (*Self, zap.Request, *ContextType) bool; + pub const RequestFn = *const fn (*Self, zap.Request, *ContextType) anyerror!bool; const Self = @This(); pub fn init(on_request: RequestFn, other: ?*Self) Self { @@ -30,7 +30,7 @@ pub fn Handler(comptime ContextType: anytype) type { // example for handling a request request // which you can use in your components, e.g.: // return self.handler.handleOther(r, context); - pub fn handleOther(self: *Self, r: zap.Request, context: *ContextType) bool { + pub fn handleOther(self: *Self, r: zap.Request, context: *ContextType) !bool { // in structs embedding a handler, we'd @fieldParentPtr the first // param to get to the real self @@ -41,7 +41,7 @@ pub fn Handler(comptime ContextType: anytype) type { var other_handler_finished = false; if (self.other_handler) |other_handler| { if (other_handler.on_request) |on_request| { - other_handler_finished = on_request(other_handler, r, context); + other_handler_finished = try on_request(other_handler, r, context); } } @@ -96,19 +96,19 @@ pub fn EndpointHandler(comptime HandlerType: anytype, comptime EndpointType: any /// /// If `breakOnFinish` is `true`, the handler will stop handing requests down the chain if /// the endpoint processed the request. - pub fn onRequest(handler: *HandlerType, r: zap.Request, context: *ContextType) bool { + pub fn onRequest(handler: *HandlerType, r: zap.Request, context: *ContextType) !bool { const self: *Self = @fieldParentPtr("handler", handler); r.setUserContext(context); if (!self.options.checkPath or std.mem.startsWith(u8, r.path orelse "", self.endpoint.path)) { switch (r.methodAsEnum()) { - .GET => self.endpoint.*.get(r), - .POST => self.endpoint.*.post(r), - .PUT => self.endpoint.*.put(r), - .DELETE => self.endpoint.*.delete(r), - .PATCH => self.endpoint.*.patch(r), - .OPTIONS => self.endpoint.*.options(r), + .GET => try self.endpoint.*.get(r), + .POST => try self.endpoint.*.post(r), + .PUT => try self.endpoint.*.put(r), + .DELETE => try self.endpoint.*.delete(r), + .PATCH => try self.endpoint.*.patch(r), + .OPTIONS => try self.endpoint.*.options(r), else => {}, } } @@ -176,7 +176,7 @@ pub fn Listener(comptime ContextType: anytype) type { /// Create your own listener if you want different behavior. /// (Didn't want to make this a callback. Submit an issue if you really /// think that's an issue). - pub fn onRequest(r: zap.Request) void { + pub fn onRequest(r: zap.Request) !void { // we are the 1st handler in the chain, so we create a context var context: ContextType = .{}; @@ -191,7 +191,7 @@ pub fn Listener(comptime ContextType: anytype) type { initial_handler.allocator = allocator; if (initial_handler.on_request) |on_request| { // we don't care about the return value at the top level - _ = on_request(initial_handler, r, &context); + _ = try on_request(initial_handler, r, &context); } } } diff --git a/src/router.zig b/src/router.zig index c9ca7d5..53500a6 100644 --- a/src/router.zig +++ b/src/router.zig @@ -21,7 +21,7 @@ pub const Options = struct { }; const CallbackTag = enum { bound, unbound }; -const BoundHandler = *fn (*const anyopaque, zap.Request) void; +const BoundHandler = *fn (*const anyopaque, zap.Request) anyerror!void; const Callback = union(CallbackTag) { bound: struct { instance: usize, handler: usize }, unbound: zap.HttpRequestFn, @@ -94,24 +94,24 @@ pub fn on_request_handler(self: *Self) zap.HttpRequestFn { return zap_on_request; } -fn zap_on_request(r: zap.Request) void { +fn zap_on_request(r: zap.Request) !void { return serve(_instance, r); } -fn serve(self: *Self, r: zap.Request) void { +fn serve(self: *Self, r: zap.Request) !void { const path = r.path orelse "/"; if (self.routes.get(path)) |routeInfo| { switch (routeInfo) { - .bound => |b| @call(.auto, @as(BoundHandler, @ptrFromInt(b.handler)), .{ @as(*anyopaque, @ptrFromInt(b.instance)), r }), - .unbound => |h| h(r), + .bound => |b| try @call(.auto, @as(BoundHandler, @ptrFromInt(b.handler)), .{ @as(*anyopaque, @ptrFromInt(b.instance)), r }), + .unbound => |h| try h(r), } } else if (self.not_found) |handler| { // not found handler - handler(r); + try handler(r); } else { // default 404 output r.setStatus(.not_found); - r.sendBody("404 Not Found") catch return; + try r.sendBody("404 Not Found"); } } diff --git a/src/tests/test_auth.zig b/src/tests/test_auth.zig index b33487d..1dba9a1 100644 --- a/src/tests/test_auth.zig +++ b/src/tests/test_auth.zig @@ -149,8 +149,9 @@ fn makeRequestThread(a: std.mem.Allocator, url: []const u8, auth: ?ClientAuthReq pub const Endpoint = struct { path: []const u8, + error_strategy: zap.Endpoint.ErrorStrategy = .raise, - pub fn get(e: *Endpoint, r: zap.Request) void { + pub fn get(e: *Endpoint, r: zap.Request) !void { _ = e; r.sendBody(HTTP_RESPONSE) catch return; received_response = HTTP_RESPONSE; @@ -158,7 +159,7 @@ pub const Endpoint = struct { zap.stop(); } - pub fn unauthorized(e: *Endpoint, r: zap.Request) void { + pub fn unauthorized(e: *Endpoint, r: zap.Request) !void { _ = e; r.setStatus(.unauthorized); r.sendBody("UNAUTHORIZED ACCESS") catch return; @@ -166,11 +167,11 @@ pub const Endpoint = struct { std.time.sleep(1 * std.time.ns_per_s); zap.stop(); } - pub fn post(_: *Endpoint, _: zap.Request) void {} - pub fn put(_: *Endpoint, _: zap.Request) void {} - pub fn delete(_: *Endpoint, _: zap.Request) void {} - pub fn patch(_: *Endpoint, _: zap.Request) void {} - pub fn options(_: *Endpoint, _: zap.Request) void {} + pub fn post(_: *Endpoint, _: zap.Request) !void {} + pub fn put(_: *Endpoint, _: zap.Request) !void {} + pub fn delete(_: *Endpoint, _: zap.Request) !void {} + pub fn patch(_: *Endpoint, _: zap.Request) !void {} + pub fn options(_: *Endpoint, _: zap.Request) !void {} }; // // end of http client code diff --git a/src/tests/test_http_params.zig b/src/tests/test_http_params.zig index 31f8b0d..167cbc0 100644 --- a/src/tests/test_http_params.zig +++ b/src/tests/test_http_params.zig @@ -30,7 +30,7 @@ test "http parameters" { var paramOneSlice: ?[]const u8 = null; var paramSlices: zap.Request.ParamSliceIterator = undefined; - pub fn on_request(r: zap.Request) void { + pub fn on_request(r: zap.Request) !void { ran = true; r.parseQuery(); param_count = r.getParamCount(); diff --git a/src/tests/test_sendfile.zig b/src/tests/test_sendfile.zig index 8b16a8a..964ee99 100644 --- a/src/tests/test_sendfile.zig +++ b/src/tests/test_sendfile.zig @@ -24,7 +24,7 @@ fn makeRequest(a: std.mem.Allocator, url: []const u8) !void { fn makeRequestThread(a: std.mem.Allocator, url: []const u8) !std.Thread { return try std.Thread.spawn(.{}, makeRequest, .{ a, url }); } -pub fn on_request(r: zap.Request) void { +pub fn on_request(r: zap.Request) !void { r.sendFile("src/tests/testfile.txt") catch unreachable; } diff --git a/src/util.zig b/src/util.zig index 4b6a4c9..663155c 100644 --- a/src/util.zig +++ b/src/util.zig @@ -74,12 +74,12 @@ pub fn stringifyBuf( buffer: []u8, value: anytype, options: std.json.StringifyOptions, -) ?[]const u8 { +) ![]const u8 { var fba = std.heap.FixedBufferAllocator.init(buffer); var string = std.ArrayList(u8).init(fba.allocator()); if (std.json.stringify(value, options, string.writer())) { return string.items; - } else |_| { // error - return null; + } else |err| { // error + return err; } } diff --git a/src/zap.zig b/src/zap.zig index bce9853..93d3ecb 100644 --- a/src/zap.zig +++ b/src/zap.zig @@ -132,7 +132,7 @@ pub const ContentType = enum { pub const FioHttpRequestFn = *const fn (r: [*c]fio.http_s) callconv(.C) void; /// Zap Http request callback function type. -pub const HttpRequestFn = *const fn (Request) void; +pub const HttpRequestFn = *const fn (Request) anyerror!void; /// websocket connection upgrade callback type /// fn(request, targetstring) @@ -202,8 +202,10 @@ pub const HttpListener = struct { req.markAsFinished(false); std.debug.assert(l.settings.on_request != null); if (l.settings.on_request) |on_request| { - // l.settings.on_request.?(req); - on_request(req); + on_request(req) catch |err| { + // TODO: log / handle the error in a better way + std.debug.print("zap on_request error: {}", .{err}); + }; } } } @@ -225,7 +227,10 @@ pub const HttpListener = struct { var user_context: Request.UserContext = .{}; req._user_context = &user_context; - l.settings.on_response.?(req); + l.settings.on_response.?(req) catch |err| { + // TODO: log / handle the error in a better way + std.debug.print("zap on_response error: {}", .{err}); + }; } } diff --git a/tools/docserver.zig b/tools/docserver.zig index e6fc5ba..5368f20 100644 --- a/tools/docserver.zig +++ b/tools/docserver.zig @@ -1,7 +1,7 @@ const std = @import("std"); const zap = @import("zap"); -fn on_request(r: zap.Request) void { +fn on_request(r: zap.Request) !void { r.setStatus(.not_found); r.sendBody("

404 - File not found

") catch return; } diff --git a/wrk/zig/main.zig b/wrk/zig/main.zig index 20fa3f3..ef68612 100644 --- a/wrk/zig/main.zig +++ b/wrk/zig/main.zig @@ -1,8 +1,8 @@ const std = @import("std"); const zap = @import("zap"); -fn on_request_minimal(r: zap.Request) void { - r.sendBody("Hello from ZAP!!!") catch return; +fn on_request_minimal(r: zap.Request) !void { + try r.sendBody("Hello from ZAP!!!"); } pub fn main() !void {