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

introduced error union to request fn return type

This commit is contained in:
renerocksai 2025-03-16 20:16:14 +01:00
parent 7503f090ee
commit fcce4517de
No known key found for this signature in database
32 changed files with 246 additions and 217 deletions

View file

@ -5,7 +5,7 @@ var gpa = std.heap.GeneralPurposeAllocator(.{
.thread_safe = true, .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 // use a local buffer for the parsed accept headers
var accept_buffer: [1024]u8 = undefined; var accept_buffer: [1024]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&accept_buffer); var fba = std.heap.FixedBufferAllocator.init(&accept_buffer);
@ -21,38 +21,38 @@ fn on_request_verbose(r: zap.Request) void {
break :content_type .HTML; break :content_type .HTML;
}; };
r.setContentType(content_type) catch return; try r.setContentType(content_type);
switch (content_type) { switch (content_type) {
.TEXT => { .TEXT => {
r.sendBody("Hello from ZAP!!!") catch return; try r.sendBody("Hello from ZAP!!!");
}, },
.HTML => { .HTML => {
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return; try r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>");
}, },
.XML => { .XML => {
r.sendBody( try r.sendBody(
\\<?xml version="1.0" encoding="UTF-8"?> \\<?xml version="1.0" encoding="UTF-8"?>
\\<message> \\<message>
\\ <warning> \\ <warning>
\\ Hello from ZAP!!! \\ Hello from ZAP!!!
\\ </warning> \\ </warning>
\\</message> \\</message>
) catch return; );
}, },
.JSON => { .JSON => {
var buffer: [128]u8 = undefined; var buffer: [128]u8 = undefined;
const json = zap.util.stringifyBuf(&buffer, .{ .message = "Hello from ZAP!!!" }, .{}) orelse return; const json = try zap.util.stringifyBuf(&buffer, .{ .message = "Hello from ZAP!!!" }, .{});
r.sendJson(json) catch return; try r.sendJson(json);
}, },
.XHTML => { .XHTML => {
r.sendBody( try r.sendBody(
\\<?xml version="1.0" encoding="UTF-8"?> \\<?xml version="1.0" encoding="UTF-8"?>
\\<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US"> \\<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
\\ <body> \\ <body>
\\ <h1>Hello from ZAP!!!</h1> \\ <h1>Hello from ZAP!!!</h1>
\\ </body> \\ </body>
\\</html> \\</html>
) catch return; );
}, },
} }
} }

View file

@ -4,7 +4,7 @@ const zap = @import("zap");
const Handler = struct { const Handler = struct {
var alloc: std.mem.Allocator = undefined; 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 // parse for FORM (body) parameters first
r.parseBody() catch |err| { r.parseBody() catch |err| {
std.log.err("Parse Body error: {any}. Expected if body is empty", .{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 // 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(); defer params.deinit();
for (params.items) |kv| { for (params.items) |kv| {
if (kv.value) |v| { if (kv.value) |v| {
@ -82,7 +82,7 @@ const Handler = struct {
} else |err| { } else |err| {
std.log.err("cannot check for terminate param: {any}\n", .{err}); std.log.err("cannot check for terminate param: {any}\n", .{err});
} }
r.sendJson("{ \"ok\": true }") catch unreachable; try r.sendJson("{ \"ok\": true }");
} }
}; };

View file

@ -35,7 +35,7 @@ pub fn main() !void {
const Handler = struct { const Handler = struct {
var alloc: std.mem.Allocator = undefined; 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", .{}); std.debug.print("\n=====================================================\n", .{});
defer std.debug.print("=====================================================\n\n", .{}); defer std.debug.print("=====================================================\n\n", .{});

View file

@ -4,12 +4,12 @@ const UserWeb = @import("userweb.zig");
const StopEndpoint = @import("stopendpoint.zig"); const StopEndpoint = @import("stopendpoint.zig");
// this is just to demo that we can catch arbitrary slugs as fallback // 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| { if (r.path) |the_path| {
std.debug.print("REQUESTED PATH: {s}\n", .{the_path}); std.debug.print("REQUESTED PATH: {s}\n", .{the_path});
} }
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return; try r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>");
} }
pub fn main() !void { pub fn main() !void {

View file

@ -6,6 +6,7 @@ const zap = @import("zap");
pub const Self = @This(); pub const Self = @This();
path: []const u8, path: []const u8,
error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response,
pub fn init(path: []const u8) Self { pub fn init(path: []const u8) Self {
return .{ 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; _ = e;
_ = r; _ = r;
zap.stop(); zap.stop();
} }
pub fn post(_: *Self, _: zap.Request) void {} pub fn post(_: *Self, _: zap.Request) anyerror!void {}
pub fn put(_: *Self, _: zap.Request) void {} pub fn put(_: *Self, _: zap.Request) anyerror!void {}
pub fn delete(_: *Self, _: zap.Request) void {} pub fn delete(_: *Self, _: zap.Request) anyerror!void {}
pub fn patch(_: *Self, _: zap.Request) void {} pub fn patch(_: *Self, _: zap.Request) anyerror!void {}
pub fn options(_: *Self, _: zap.Request) void {} pub fn options(_: *Self, _: zap.Request) anyerror!void {}

View file

@ -11,6 +11,7 @@ alloc: std.mem.Allocator = undefined,
_users: Users = undefined, _users: Users = undefined,
path: []const u8, path: []const u8,
error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response,
pub fn init( pub fn init(
a: std.mem.Allocator, a: std.mem.Allocator,
@ -42,8 +43,8 @@ fn userIdFromPath(self: *Self, path: []const u8) ?usize {
return null; return null;
} }
pub fn put(_: *Self, _: zap.Request) void {} pub fn put(_: *Self, _: zap.Request) anyerror!void {}
pub fn get(self: *Self, r: zap.Request) void { pub fn get(self: *Self, r: zap.Request) anyerror!void {
if (r.path) |path| { if (r.path) |path| {
// /users // /users
if (path.len == self.path.len) { if (path.len == self.path.len) {
@ -52,33 +53,31 @@ pub fn get(self: *Self, r: zap.Request) void {
var jsonbuf: [256]u8 = undefined; var jsonbuf: [256]u8 = undefined;
if (self.userIdFromPath(path)) |id| { if (self.userIdFromPath(path)) |id| {
if (self._users.get(id)) |user| { if (self._users.get(id)) |user| {
if (zap.util.stringifyBuf(&jsonbuf, user, .{})) |json| { const json = try zap.util.stringifyBuf(&jsonbuf, user, .{});
r.sendJson(json) catch return; 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| { if (self._users.toJSON()) |json| {
defer self.alloc.free(json); defer self.alloc.free(json);
r.sendJson(json) catch return; try r.sendJson(json);
} else |err| { } 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| { if (r.body) |body| {
const maybe_user: ?std.json.Parsed(User) = std.json.parseFromSlice(User, self.alloc, body, .{}) catch null; const maybe_user: ?std.json.Parsed(User) = std.json.parseFromSlice(User, self.alloc, body, .{}) catch null;
if (maybe_user) |u| { if (maybe_user) |u| {
defer u.deinit(); defer u.deinit();
if (self._users.addByName(u.value.first_name, u.value.last_name)) |id| { if (self._users.addByName(u.value.first_name, u.value.last_name)) |id| {
var jsonbuf: [128]u8 = undefined; var jsonbuf: [128]u8 = undefined;
if (zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| { const json = try zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{});
r.sendJson(json) catch return; try r.sendJson(json);
}
} else |err| { } else |err| {
std.debug.print("ADDING error: {}\n", .{err}); std.debug.print("ADDING error: {}\n", .{err});
return; 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 (r.path) |path| {
if (self.userIdFromPath(path)) |id| { if (self.userIdFromPath(path)) |id| {
if (self._users.get(id)) |_| { if (self._users.get(id)) |_| {
@ -97,13 +96,11 @@ pub fn patch(self: *Self, r: zap.Request) void {
defer u.deinit(); defer u.deinit();
var jsonbuf: [128]u8 = undefined; var jsonbuf: [128]u8 = undefined;
if (self._users.update(id, u.value.first_name, u.value.last_name)) { if (self._users.update(id, u.value.first_name, u.value.last_name)) {
if (zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| { const json = try zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{});
r.sendJson(json) catch return; try r.sendJson(json);
}
} else { } else {
if (zap.util.stringifyBuf(&jsonbuf, .{ .status = "ERROR", .id = id }, .{})) |json| { const json = try zap.util.stringifyBuf(&jsonbuf, .{ .status = "ERROR", .id = id }, .{});
r.sendJson(json) catch return; 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 (r.path) |path| {
if (self.userIdFromPath(path)) |id| { if (self.userIdFromPath(path)) |id| {
var jsonbuf: [128]u8 = undefined; var jsonbuf: [128]u8 = undefined;
if (self._users.delete(id)) { if (self._users.delete(id)) {
if (zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{})) |json| { const json = try zap.util.stringifyBuf(&jsonbuf, .{ .status = "OK", .id = id }, .{});
r.sendJson(json) catch return; try r.sendJson(json);
}
} else { } else {
if (zap.util.stringifyBuf(&jsonbuf, .{ .status = "ERROR", .id = id }, .{})) |json| { const json = try zap.util.stringifyBuf(&jsonbuf, .{ .status = "ERROR", .id = id }, .{});
r.sendJson(json) catch return; try r.sendJson(json);
}
} }
} }
} }
} }
pub fn options(_: *Self, r: zap.Request) void { pub fn options(_: *Self, r: zap.Request) anyerror!void {
r.setHeader("Access-Control-Allow-Origin", "*") catch return; try r.setHeader("Access-Control-Allow-Origin", "*");
r.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") catch return; try r.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
r.setStatus(zap.http.StatusCode.no_content); r.setStatus(zap.http.StatusCode.no_content);
r.markAsFinished(true); r.markAsFinished(true);
} }

View file

@ -13,24 +13,25 @@ const HTTP_RESPONSE: []const u8 =
const Endpoint = struct { const Endpoint = struct {
// the slug // the slug
path: []const u8, path: []const u8,
error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response,
// authenticated requests go here // 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; r.sendBody(HTTP_RESPONSE) catch return;
} }
// just for fun, we also catch the unauthorized callback // 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.setStatus(.unauthorized);
r.sendBody("UNAUTHORIZED ACCESS") catch return; r.sendBody("UNAUTHORIZED ACCESS") catch return;
} }
// not implemented, don't care // not implemented, don't care
pub fn post(_: *Endpoint, _: zap.Request) void {} pub fn post(_: *Endpoint, _: zap.Request) !void {}
pub fn put(_: *Endpoint, _: zap.Request) void {} pub fn put(_: *Endpoint, _: zap.Request) !void {}
pub fn delete(_: *Endpoint, _: zap.Request) void {} pub fn delete(_: *Endpoint, _: zap.Request) !void {}
pub fn patch(_: *Endpoint, _: zap.Request) void {} pub fn patch(_: *Endpoint, _: zap.Request) !void {}
pub fn options(_: *Endpoint, _: zap.Request) void {} pub fn options(_: *Endpoint, _: zap.Request) !void {}
}; };
pub fn main() !void { pub fn main() !void {

View file

@ -1,7 +1,7 @@
const std = @import("std"); const std = @import("std");
const zap = @import("zap"); 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| { if (r.path) |the_path| {
std.debug.print("PATH: {s}\n", .{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| { if (r.query) |the_query| {
std.debug.print("QUERY: {s}\n", .{the_query}); std.debug.print("QUERY: {s}\n", .{the_query});
} }
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return; try r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>");
} }
fn on_request_minimal(r: zap.Request) void { fn on_request_minimal(r: zap.Request) !void {
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return; try r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>");
} }
pub fn main() !void { pub fn main() !void {

View file

@ -1,7 +1,7 @@
const std = @import("std"); const std = @import("std");
const zap = @import("zap"); const zap = @import("zap");
fn on_request(r: zap.Request) void { fn on_request(r: zap.Request) !void {
const m = r.methodAsEnum(); const m = r.methodAsEnum();
const m_str = r.method orelse ""; const m_str = r.method orelse "";
const p = r.path 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}); std.debug.print(">> BODY: {s}\n", .{the_body});
} }
r.setContentTypeFromPath() catch return; try r.setContentTypeFromPath();
r.sendBody( try r.sendBody(
\\ <html><body> \\ <html><body>
\\ <h1>Hello from ZAP!!!</h1> \\ <h1>Hello from ZAP!!!</h1>
\\ <form action="/" method="post"> \\ <form action="/" method="post">
@ -32,7 +32,7 @@ fn on_request(r: zap.Request) void {
\\ <button>Send</button> \\ <button>Send</button>
\\ </form> \\ </form>
\\ </body></html> \\ </body></html>
) catch return; );
} }
pub fn main() !void { pub fn main() !void {

View file

@ -6,7 +6,7 @@ const User = struct {
last_name: ?[]const u8 = null, last_name: ?[]const u8 = null,
}; };
fn on_request(r: zap.Request) void { fn on_request(r: zap.Request) !void {
if (r.methodAsEnum() != .GET) return; if (r.methodAsEnum() != .GET) return;
// /user/n // /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/")) if (the_path.len < 7 or !std.mem.startsWith(u8, the_path, "/user/"))
return; 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); 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; var json_to_send: []const u8 = undefined;
if (zap.util.stringifyBuf(&buf, user, .{})) |json| { json_to_send = try zap.util.stringifyBuf(&buf, user, .{});
json_to_send = json;
} else {
json_to_send = "null";
}
std.debug.print("<< json: {s}\n", .{json_to_send}); std.debug.print("<< json: {s}\n", .{json_to_send});
r.setContentType(.JSON) catch return; r.setContentType(.JSON) catch return;
r.setContentTypeFromFilename("test.json") catch return; r.setContentTypeFromFilename("test.json") catch return;

View file

@ -32,7 +32,7 @@ pub fn main() !void {
const Handler = struct { const Handler = struct {
var alloc: std.mem.Allocator = undefined; 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", .{}); std.debug.print("\n=====================================================\n", .{});
defer 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 // iterate over all params as strings
var strparams = r.parametersToOwnedStrList(alloc, false) catch unreachable; var strparams = try r.parametersToOwnedStrList(alloc, false);
defer strparams.deinit(); defer strparams.deinit();
std.debug.print("\n", .{}); std.debug.print("\n", .{});
for (strparams.items) |kv| { for (strparams.items) |kv| {
@ -79,7 +79,7 @@ pub fn main() !void {
std.debug.print("\n", .{}); std.debug.print("\n", .{});
// iterate over all params // iterate over all params
const params = r.parametersToOwnedList(alloc, false) catch unreachable; const params = try r.parametersToOwnedList(alloc, false);
defer params.deinit(); defer params.deinit();
for (params.items) |kv| { for (params.items) |kv| {
std.log.info("Param `{s}` is {any}", .{ kv.key.str, kv.value }); std.log.info("Param `{s}` is {any}", .{ kv.key.str, kv.value });

View file

@ -1,7 +1,7 @@
const std = @import("std"); const std = @import("std");
const zap = @import("zap"); 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| { if (r.path) |the_path| {
std.debug.print("PATH: {s}\n", .{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| { if (r.query) |the_query| {
std.debug.print("QUERY: {s}\n", .{the_query}); std.debug.print("QUERY: {s}\n", .{the_query});
} }
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return; try r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>");
} }
fn on_request_minimal(r: zap.Request) void { fn on_request_minimal(r: zap.Request) !void {
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return; try r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>");
} }
fn help_and_exit(filename: []const u8, err: anyerror) void { fn help_and_exit(filename: []const u8, err: anyerror) void {

View file

@ -69,7 +69,7 @@ const UserMiddleWare = struct {
} }
// note that the first parameter is of type *Handler, not *Self !!! // 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 // this is how we would get our self pointer
const self: *Self = @fieldParentPtr("handler", handler); const self: *Self = @fieldParentPtr("handler", handler);
@ -113,7 +113,7 @@ const SessionMiddleWare = struct {
} }
// note that the first parameter is of type *Handler, not *Self !!! // 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 // this is how we would get our self pointer
const self: *Self = @fieldParentPtr("handler", handler); const self: *Self = @fieldParentPtr("handler", handler);
_ = self; _ = self;
@ -148,7 +148,7 @@ const HtmlMiddleWare = struct {
} }
// note that the first parameter is of type *Handler, not *Self !!! // 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 // this is how we would get our self pointer
const self: *Self = @fieldParentPtr("handler", handler); const self: *Self = @fieldParentPtr("handler", handler);

View file

@ -57,7 +57,7 @@ const UserMiddleWare = struct {
} }
// note that the first parameter is of type *Handler, not *Self !!! // 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 // this is how we would get our self pointer
const self: *Self = @fieldParentPtr("handler", handler); const self: *Self = @fieldParentPtr("handler", handler);
@ -103,7 +103,7 @@ const SessionMiddleWare = struct {
} }
// note that the first parameter is of type *Handler, not *Self !!! // 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 // this is how we would get our self pointer
const self: *Self = @fieldParentPtr("handler", handler); const self: *Self = @fieldParentPtr("handler", handler);
_ = self; _ = self;
@ -146,13 +146,13 @@ const HtmlEndpoint = struct {
}; };
} }
pub fn post(_: *HtmlEndpoint, _: zap.Request) void {} pub fn post(_: *HtmlEndpoint, _: zap.Request) !void {}
pub fn put(_: *HtmlEndpoint, _: zap.Request) void {} pub fn put(_: *HtmlEndpoint, _: zap.Request) !void {}
pub fn delete(_: *HtmlEndpoint, _: zap.Request) void {} pub fn delete(_: *HtmlEndpoint, _: zap.Request) !void {}
pub fn patch(_: *HtmlEndpoint, _: zap.Request) void {} pub fn patch(_: *HtmlEndpoint, _: zap.Request) !void {}
pub fn options(_: *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 buf: [1024]u8 = undefined;
var userFound: bool = false; var userFound: bool = false;
var sessionFound: bool = false; var sessionFound: bool = false;
@ -169,12 +169,12 @@ const HtmlEndpoint = struct {
sessionFound = true; sessionFound = true;
std.debug.assert(r.isFinished() == false); 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.name,
user.email, user.email,
session.info, session.info,
session.token, session.token,
}) catch unreachable; });
r.setContentType(.TEXT) catch unreachable; r.setContentType(.TEXT) catch unreachable;
r.sendBody(message) catch unreachable; r.sendBody(message) catch unreachable;
std.debug.assert(r.isFinished() == true); std.debug.assert(r.isFinished() == true);

View file

@ -2,7 +2,7 @@ const std = @import("std");
const zap = @import("zap"); const zap = @import("zap");
const Mustache = @import("zap").Mustache; const Mustache = @import("zap").Mustache;
fn on_request(r: zap.Request) void { fn on_request(r: zap.Request) !void {
const template = const template =
\\ {{=<< >>=}} \\ {{=<< >>=}}
\\ * Users: \\ * Users:

View file

@ -3,39 +3,39 @@ const zap = @import("zap");
// NOTE: this is a super simplified example, just using a hashmap to map // NOTE: this is a super simplified example, just using a hashmap to map
// from HTTP path to request function. // from HTTP path to request function.
fn dispatch_routes(r: zap.Request) void { fn dispatch_routes(r: zap.Request) !void {
// dispatch // dispatch
if (r.path) |the_path| { if (r.path) |the_path| {
if (routes.get(the_path)) |foo| { if (routes.get(the_path)) |foo| {
foo(r); try foo(r);
return; return;
} }
} }
// or default: present menu // or default: present menu
r.sendBody( try r.sendBody(
\\ <html> \\ <html>
\\ <body> \\ <body>
\\ <p><a href="/static">static</a></p> \\ <p><a href="/static">static</a></p>
\\ <p><a href="/dynamic">dynamic</a></p> \\ <p><a href="/dynamic">dynamic</a></p>
\\ </body> \\ </body>
\\ </html> \\ </html>
) catch return; );
} }
fn static_site(r: zap.Request) void { fn static_site(r: zap.Request) !void {
r.sendBody("<html><body><h1>Hello from STATIC ZAP!</h1></body></html>") catch return; try r.sendBody("<html><body><h1>Hello from STATIC ZAP!</h1></body></html>");
} }
var dynamic_counter: i32 = 0; var dynamic_counter: i32 = 0;
fn dynamic_site(r: zap.Request) void { fn dynamic_site(r: zap.Request) !void {
dynamic_counter += 1; dynamic_counter += 1;
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
const filled_buf = std.fmt.bufPrintZ( const filled_buf = try std.fmt.bufPrintZ(
&buf, &buf,
"<html><body><h1>Hello # {d} from DYNAMIC ZAP!!!</h1></body></html>", "<html><body><h1>Hello # {d} from DYNAMIC ZAP!!!</h1></body></html>",
.{dynamic_counter}, .{dynamic_counter},
) catch "ERROR"; );
r.sendBody(filled_buf) catch return; try r.sendBody(filled_buf);
} }
fn setup_routes(a: std.mem.Allocator) !void { fn setup_routes(a: std.mem.Allocator) !void {

View file

@ -5,7 +5,7 @@ fn MAKE_MEGA_ERROR() !void {
return error.MEGA_ERROR; 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| { MAKE_MEGA_ERROR() catch |err| {
r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505); r.sendError(err, if (@errorReturnTrace()) |t| t.* else null, 505);
}; };

View file

@ -6,7 +6,7 @@ var read_len: ?usize = null;
const testfile = @embedFile("testfile.txt"); 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. // Sends a file if present in the filesystem orelse returns an error.
// //
// - efficiently sends a file using gzip compression // - efficiently sends a file using gzip compression

View file

@ -1,7 +1,7 @@
const std = @import("std"); const std = @import("std");
const zap = @import("zap"); const zap = @import("zap");
fn on_request(r: zap.Request) void { fn on_request(r: zap.Request) !void {
r.setStatus(.not_found); r.setStatus(.not_found);
r.sendBody("<html><body><h1>404 - File not found</h1></body></html>") catch return; r.sendBody("<html><body><h1>404 - File not found</h1></body></html>") catch return;
} }

View file

@ -2,7 +2,7 @@ const std = @import("std");
const zap = @import("zap"); const zap = @import("zap");
const Allocator = std.mem.Allocator; 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| { if (r.path) |the_path| {
std.debug.print("PATH: {s}\n", .{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", .{}); std.log.warn("get_a_requested", .{});
const string = std.fmt.allocPrint( const string = std.fmt.allocPrint(
@ -41,7 +41,7 @@ pub const SomePackage = struct {
req.sendBody(string) catch return; 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", .{}); std.log.warn("get_b_requested", .{});
const string = std.fmt.allocPrint( const string = std.fmt.allocPrint(
@ -54,7 +54,7 @@ pub const SomePackage = struct {
req.sendBody(string) catch return; 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", .{}); std.log.warn("increment_a_requested", .{});
self.a += 1; 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", .{}); std.debug.print("not found handler", .{});
req.sendBody("Not found") catch return; try req.sendBody("Not found");
} }
pub fn main() !void { pub fn main() !void {

View file

@ -25,38 +25,38 @@ const img = @embedFile("./html/Ziggy_the_Ziguana.svg.png");
var authenticator: Authenticator = undefined; var authenticator: Authenticator = undefined;
// the login page (embedded) // the login page (embedded)
fn on_login(r: zap.Request) void { fn on_login(r: zap.Request) !void {
r.sendBody(loginpage) catch return; try r.sendBody(loginpage);
} }
// the "normal page" // 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", .{}); zap.debug("on_normal_page()\n", .{});
r.sendBody( try r.sendBody(
\\ <html><body> \\ <html><body>
\\ <h1>Hello from ZAP!!!</h1> \\ <h1>Hello from ZAP!!!</h1>
\\ <p>You are logged in!!!</> \\ <p>You are logged in!!!</>
\\ <center><a href="/logout">logout</a></center> \\ <center><a href="/logout">logout</a></center>
\\ </body></html> \\ </body></html>
) catch return; );
} }
// the logged-out page // the logged-out page
fn on_logout(r: zap.Request) void { fn on_logout(r: zap.Request) !void {
zap.debug("on_logout()\n", .{}); zap.debug("on_logout()\n", .{});
authenticator.logout(&r); authenticator.logout(&r);
// note, the link below doesn't matter as the authenticator will send us // note, the link below doesn't matter as the authenticator will send us
// straight to the /login page // straight to the /login page
r.sendBody( try r.sendBody(
\\ <html><body> \\ <html><body>
\\ <p>You are logged out!!!</p> \\ <p>You are logged out!!!</p>
\\ <br> \\ <br>
\\ <p> <a href="/">Log back in</a></p> \\ <p> <a href="/">Log back in</a></p>
\\ </body></html> \\ </body></html>
) catch return; );
} }
fn on_request(r: zap.Request) void { fn on_request(r: zap.Request) !void {
switch (authenticator.authenticateRequest(&r)) { switch (authenticator.authenticateRequest(&r)) {
.Handled => { .Handled => {
// the authenticator handled the entire request for us. // 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 // the authenticator. Hence, we name the img for the /login
// page: /login/Ziggy....png // page: /login/Ziggy....png
if (std.mem.startsWith(u8, p, "/login/Ziggy_the_Ziguana.svg.png")) { if (std.mem.startsWith(u8, p, "/login/Ziggy_the_Ziguana.svg.png")) {
r.setContentTypeFromPath() catch unreachable; try r.setContentTypeFromPath();
r.sendBody(img) catch unreachable; try r.sendBody(img);
return; return;
} }

View file

@ -163,13 +163,13 @@ fn handle_websocket_message(
// //
// HTTP stuff // HTTP stuff
// //
fn on_request(r: zap.Request) void { fn on_request(r: zap.Request) !void {
r.setHeader("Server", "zap.example") catch unreachable; r.setHeader("Server", "zap.example") catch unreachable;
r.sendBody( try r.sendBody(
\\ <html><body> \\ <html><body>
\\ <h1>This is a simple Websocket chatroom example</h1> \\ <h1>This is a simple Websocket chatroom example</h1>
\\ </body></html> \\ </body></html>
) catch return; );
} }
fn on_upgrade(r: zap.Request, target_protocol: []const u8) void { fn on_upgrade(r: zap.Request, target_protocol: []const u8) void {

View file

@ -55,6 +55,16 @@ const std = @import("std");
const zap = @import("zap.zig"); const zap = @import("zap.zig");
const auth = @import("http_auth.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 // zap types
const Request = zap.Request; const Request = zap.Request;
const ListenerSettings = zap.HttpListenerSettings; const ListenerSettings = zap.HttpListenerSettings;
@ -69,6 +79,14 @@ pub fn checkEndpointType(T: type) void {
@compileError(@typeName(T) ++ " has no path field"); @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{ const methods_to_check = [_][]const u8{
"get", "get",
"post", "post",
@ -79,8 +97,8 @@ pub fn checkEndpointType(T: type) void {
}; };
inline for (methods_to_check) |method| { inline for (methods_to_check) |method| {
if (@hasDecl(T, method)) { if (@hasDecl(T, method)) {
if (@TypeOf(@field(T, method)) != 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) void)); @compileError(method ++ " method of " ++ @typeName(T) ++ " has wrong type:\n" ++ @typeName(@TypeOf(T.get)) ++ "\nexpected:\n" ++ @typeName(fn (_: *T, _: Request) anyerror!void));
} }
} else { } else {
@compileError(@typeName(T) ++ " has no method named `" ++ method ++ "`"); @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 { pub const Wrapper = struct {
call: *const fn (*Wrapper, zap.Request) void = undefined, pub const Internal = struct {
call: *const fn (*Internal, zap.Request) anyerror!void = undefined,
path: []const u8, 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 { pub fn Wrap(T: type) type {
return struct { return struct {
wrapped: *T, wrapped: *T,
wrapper: Wrapper, wrapper: Internal,
const Self = @This(); const Self = @This();
pub fn unwrap(wrapper: *Wrapper) *Self { pub fn unwrap(wrapper: *Internal) *Self {
const self: *Self = @alignCast(@fieldParentPtr("wrapper", wrapper)); const self: *Self = @alignCast(@fieldParentPtr("wrapper", wrapper));
return self; 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)); const self: *Self = @alignCast(@fieldParentPtr("wrapper", wrapper));
allocator.destroy(self); 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); var self: *Self = Self.unwrap(wrapper);
self.onRequest(r); try self.onRequest(r);
} }
pub fn onRequest(self: *Self, r: zap.Request) void { pub fn onRequest(self: *Self, r: zap.Request) !void {
switch (r.methodAsEnum()) { const ret = switch (r.methodAsEnum()) {
.GET => return self.wrapped.*.get(r), .GET => self.wrapped.*.get(r),
.POST => return self.wrapped.*.post(r), .POST => self.wrapped.*.post(r),
.PUT => return self.wrapped.*.put(r), .PUT => self.wrapped.*.put(r),
.DELETE => return self.wrapped.*.delete(r), .DELETE => self.wrapped.*.delete(r),
.PATCH => return self.wrapped.*.patch(r), .PATCH => self.wrapped.*.patch(r),
.OPTIONS => return self.wrapped.*.options(r), .OPTIONS => self.wrapped.*.options(r),
else => { else => error.UnsupportedHtmlRequestMethod,
// TODO: log that method is ignored };
}, 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); checkEndpointType(T);
var ret: EndpointWrapper.Wrap(T) = .{ var ret: Wrapper.Wrap(T) = .{
.wrapped = value, .wrapped = value,
.wrapper = .{ .path = value.path }, .wrapper = .{ .path = value.path },
}; };
ret.wrapper.call = EndpointWrapper.Wrap(T).onRequestWrapped; ret.wrapper.call = Wrapper.Wrap(T).onRequestWrapped;
ret.wrapper.destroy = EndpointWrapper.Wrap(T).destroy; ret.wrapper.destroy = Wrapper.Wrap(T).destroy;
return ret; return ret;
} }
}; };
@ -150,6 +175,7 @@ pub fn Authenticating(EndpointType: type, Authenticator: type) type {
authenticator: *Authenticator, authenticator: *Authenticator,
ep: *EndpointType, ep: *EndpointType,
path: []const u8, path: []const u8,
error_strategy: ErrorStrategy,
const Self = @This(); const Self = @This();
/// Init the authenticating endpoint. Pass in a pointer to the endpoint /// 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, .authenticator = authenticator,
.ep = e, .ep = e,
.path = e.path, .path = e.path,
.error_strategy = e.error_strategy,
}; };
} }
/// Authenticates GET requests using the Authenticator. /// Authenticates GET requests using the Authenticator.
pub fn get(self: *Self, r: zap.Request) void { pub fn get(self: *Self, r: zap.Request) anyerror!void {
switch (self.authenticator.authenticateRequest(&r)) { try switch (self.authenticator.authenticateRequest(&r)) {
.AuthFailed => return self.ep.*.unauthorized(r), .AuthFailed => return self.ep.*.unauthorized(r),
.AuthOK => self.ep.*.get(r), .AuthOK => self.ep.*.get(r),
.Handled => {}, .Handled => {},
} };
} }
/// Authenticates POST requests using the Authenticator. /// Authenticates POST requests using the Authenticator.
pub fn post(self: *Self, r: zap.Request) void { pub fn post(self: *Self, r: zap.Request) anyerror!void {
switch (self.authenticator.authenticateRequest(&r)) { try switch (self.authenticator.authenticateRequest(&r)) {
.AuthFailed => return self.ep.*.unauthorized(r), .AuthFailed => return self.ep.*.unauthorized(r),
.AuthOK => self.ep.*.post(r), .AuthOK => self.ep.*.post(r),
.Handled => {}, .Handled => {},
} };
} }
/// Authenticates PUT requests using the Authenticator. /// Authenticates PUT requests using the Authenticator.
pub fn put(self: *Self, r: zap.Request) void { pub fn put(self: *Self, r: zap.Request) anyerror!void {
switch (self.authenticator.authenticateRequest(&r)) { try switch (self.authenticator.authenticateRequest(&r)) {
.AuthFailed => return self.ep.*.unauthorized(r), .AuthFailed => return self.ep.*.unauthorized(r),
.AuthOK => self.ep.*.put(r), .AuthOK => self.ep.*.put(r),
.Handled => {}, .Handled => {},
} };
} }
/// Authenticates DELETE requests using the Authenticator. /// Authenticates DELETE requests using the Authenticator.
pub fn delete(self: *Self, r: zap.Request) void { pub fn delete(self: *Self, r: zap.Request) anyerror!void {
switch (self.authenticator.authenticateRequest(&r)) { try switch (self.authenticator.authenticateRequest(&r)) {
.AuthFailed => return self.ep.*.unauthorized(r), .AuthFailed => return self.ep.*.unauthorized(r),
.AuthOK => self.ep.*.delete(r), .AuthOK => self.ep.*.delete(r),
.Handled => {}, .Handled => {},
} };
} }
/// Authenticates PATCH requests using the Authenticator. /// Authenticates PATCH requests using the Authenticator.
pub fn patch(self: *Self, r: zap.Request) void { pub fn patch(self: *Self, r: zap.Request) anyerror!void {
switch (self.authenticator.authenticateRequest(&r)) { try switch (self.authenticator.authenticateRequest(&r)) {
.AuthFailed => return self.ep.*.unauthorized(r), .AuthFailed => return self.ep.*.unauthorized(r),
.AuthOK => self.ep.*.patch(r), .AuthOK => self.ep.*.patch(r),
.Handled => {}, .Handled => {},
} };
} }
/// Authenticates OPTIONS requests using the Authenticator. /// Authenticates OPTIONS requests using the Authenticator.
pub fn options(self: *Self, r: zap.Request) void { pub fn options(self: *Self, r: zap.Request) anyerror!void {
switch (self.authenticator.authenticateRequest(&r)) { try switch (self.authenticator.authenticateRequest(&r)) {
.AuthFailed => return self.ep.*.unauthorized(r), .AuthFailed => return self.ep.*.unauthorized(r),
.AuthOK => self.ep.*.put(r), .AuthOK => self.ep.*.put(r),
.Handled => {}, .Handled => {},
} };
} }
}; };
} }
@ -237,7 +264,7 @@ pub const Listener = struct {
const Self = @This(); const Self = @This();
/// Internal static struct of member endpoints /// 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, /// Internal, static request handler callback. Will be set to the optional,
/// user-defined request callback that only gets called if no endpoints match /// 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; const EndpointType = @typeInfo(@TypeOf(e)).pointer.child;
checkEndpointType(EndpointType); checkEndpointType(EndpointType);
const wrapper = try self.allocator.create(EndpointWrapper.Wrap(EndpointType)); const wrapper = try self.allocator.create(Wrapper.Wrap(EndpointType));
wrapper.* = EndpointWrapper.init(EndpointType, e); wrapper.* = Wrapper.init(EndpointType, e);
try endpoints.append(self.allocator, &wrapper.wrapper); try endpoints.append(self.allocator, &wrapper.wrapper);
} }
fn onRequest(r: Request) void { fn onRequest(r: Request) !void {
if (r.path) |p| { if (r.path) |p| {
for (endpoints.items) |wrapper| { for (endpoints.items) |wrapper| {
if (std.mem.startsWith(u8, p, wrapper.path)) { if (std.mem.startsWith(u8, p, wrapper.path)) {
wrapper.call(wrapper, r); return try wrapper.call(wrapper, r);
return;
} }
} }
} }
// if set, call the user-provided default callback // if set, call the user-provided default callback
if (on_request) |foo| { if (on_request) |foo| {
foo(r); try foo(r);
} }
} }
}; };

View file

@ -17,7 +17,7 @@ pub fn Handler(comptime ContextType: anytype) type {
// will be set // will be set
allocator: ?std.mem.Allocator = null, 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(); const Self = @This();
pub fn init(on_request: RequestFn, other: ?*Self) Self { 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 // example for handling a request request
// which you can use in your components, e.g.: // which you can use in your components, e.g.:
// return self.handler.handleOther(r, context); // 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 // in structs embedding a handler, we'd @fieldParentPtr the first
// param to get to the real self // param to get to the real self
@ -41,7 +41,7 @@ pub fn Handler(comptime ContextType: anytype) type {
var other_handler_finished = false; var other_handler_finished = false;
if (self.other_handler) |other_handler| { if (self.other_handler) |other_handler| {
if (other_handler.on_request) |on_request| { 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 /// If `breakOnFinish` is `true`, the handler will stop handing requests down the chain if
/// the endpoint processed the request. /// 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); const self: *Self = @fieldParentPtr("handler", handler);
r.setUserContext(context); r.setUserContext(context);
if (!self.options.checkPath or if (!self.options.checkPath or
std.mem.startsWith(u8, r.path orelse "", self.endpoint.path)) std.mem.startsWith(u8, r.path orelse "", self.endpoint.path))
{ {
switch (r.methodAsEnum()) { switch (r.methodAsEnum()) {
.GET => self.endpoint.*.get(r), .GET => try self.endpoint.*.get(r),
.POST => self.endpoint.*.post(r), .POST => try self.endpoint.*.post(r),
.PUT => self.endpoint.*.put(r), .PUT => try self.endpoint.*.put(r),
.DELETE => self.endpoint.*.delete(r), .DELETE => try self.endpoint.*.delete(r),
.PATCH => self.endpoint.*.patch(r), .PATCH => try self.endpoint.*.patch(r),
.OPTIONS => self.endpoint.*.options(r), .OPTIONS => try self.endpoint.*.options(r),
else => {}, else => {},
} }
} }
@ -176,7 +176,7 @@ pub fn Listener(comptime ContextType: anytype) type {
/// Create your own listener if you want different behavior. /// Create your own listener if you want different behavior.
/// (Didn't want to make this a callback. Submit an issue if you really /// (Didn't want to make this a callback. Submit an issue if you really
/// think that's an issue). /// 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 // we are the 1st handler in the chain, so we create a context
var context: ContextType = .{}; var context: ContextType = .{};
@ -191,7 +191,7 @@ pub fn Listener(comptime ContextType: anytype) type {
initial_handler.allocator = allocator; initial_handler.allocator = allocator;
if (initial_handler.on_request) |on_request| { if (initial_handler.on_request) |on_request| {
// we don't care about the return value at the top level // 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);
} }
} }
} }

View file

@ -21,7 +21,7 @@ pub const Options = struct {
}; };
const CallbackTag = enum { bound, unbound }; 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) { const Callback = union(CallbackTag) {
bound: struct { instance: usize, handler: usize }, bound: struct { instance: usize, handler: usize },
unbound: zap.HttpRequestFn, unbound: zap.HttpRequestFn,
@ -94,24 +94,24 @@ pub fn on_request_handler(self: *Self) zap.HttpRequestFn {
return zap_on_request; return zap_on_request;
} }
fn zap_on_request(r: zap.Request) void { fn zap_on_request(r: zap.Request) !void {
return serve(_instance, r); 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 "/"; const path = r.path orelse "/";
if (self.routes.get(path)) |routeInfo| { if (self.routes.get(path)) |routeInfo| {
switch (routeInfo) { switch (routeInfo) {
.bound => |b| @call(.auto, @as(BoundHandler, @ptrFromInt(b.handler)), .{ @as(*anyopaque, @ptrFromInt(b.instance)), r }), .bound => |b| try @call(.auto, @as(BoundHandler, @ptrFromInt(b.handler)), .{ @as(*anyopaque, @ptrFromInt(b.instance)), r }),
.unbound => |h| h(r), .unbound => |h| try h(r),
} }
} else if (self.not_found) |handler| { } else if (self.not_found) |handler| {
// not found handler // not found handler
handler(r); try handler(r);
} else { } else {
// default 404 output // default 404 output
r.setStatus(.not_found); r.setStatus(.not_found);
r.sendBody("404 Not Found") catch return; try r.sendBody("404 Not Found");
} }
} }

View file

@ -149,8 +149,9 @@ fn makeRequestThread(a: std.mem.Allocator, url: []const u8, auth: ?ClientAuthReq
pub const Endpoint = struct { pub const Endpoint = struct {
path: []const u8, 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; _ = e;
r.sendBody(HTTP_RESPONSE) catch return; r.sendBody(HTTP_RESPONSE) catch return;
received_response = HTTP_RESPONSE; received_response = HTTP_RESPONSE;
@ -158,7 +159,7 @@ pub const Endpoint = struct {
zap.stop(); zap.stop();
} }
pub fn unauthorized(e: *Endpoint, r: zap.Request) void { pub fn unauthorized(e: *Endpoint, r: zap.Request) !void {
_ = e; _ = e;
r.setStatus(.unauthorized); r.setStatus(.unauthorized);
r.sendBody("UNAUTHORIZED ACCESS") catch return; r.sendBody("UNAUTHORIZED ACCESS") catch return;
@ -166,11 +167,11 @@ pub const Endpoint = struct {
std.time.sleep(1 * std.time.ns_per_s); std.time.sleep(1 * std.time.ns_per_s);
zap.stop(); zap.stop();
} }
pub fn post(_: *Endpoint, _: zap.Request) void {} pub fn post(_: *Endpoint, _: zap.Request) !void {}
pub fn put(_: *Endpoint, _: zap.Request) void {} pub fn put(_: *Endpoint, _: zap.Request) !void {}
pub fn delete(_: *Endpoint, _: zap.Request) void {} pub fn delete(_: *Endpoint, _: zap.Request) !void {}
pub fn patch(_: *Endpoint, _: zap.Request) void {} pub fn patch(_: *Endpoint, _: zap.Request) !void {}
pub fn options(_: *Endpoint, _: zap.Request) void {} pub fn options(_: *Endpoint, _: zap.Request) !void {}
}; };
// //
// end of http client code // end of http client code

View file

@ -30,7 +30,7 @@ test "http parameters" {
var paramOneSlice: ?[]const u8 = null; var paramOneSlice: ?[]const u8 = null;
var paramSlices: zap.Request.ParamSliceIterator = undefined; var paramSlices: zap.Request.ParamSliceIterator = undefined;
pub fn on_request(r: zap.Request) void { pub fn on_request(r: zap.Request) !void {
ran = true; ran = true;
r.parseQuery(); r.parseQuery();
param_count = r.getParamCount(); param_count = r.getParamCount();

View file

@ -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 { fn makeRequestThread(a: std.mem.Allocator, url: []const u8) !std.Thread {
return try std.Thread.spawn(.{}, makeRequest, .{ a, url }); 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; r.sendFile("src/tests/testfile.txt") catch unreachable;
} }

View file

@ -74,12 +74,12 @@ pub fn stringifyBuf(
buffer: []u8, buffer: []u8,
value: anytype, value: anytype,
options: std.json.StringifyOptions, options: std.json.StringifyOptions,
) ?[]const u8 { ) ![]const u8 {
var fba = std.heap.FixedBufferAllocator.init(buffer); var fba = std.heap.FixedBufferAllocator.init(buffer);
var string = std.ArrayList(u8).init(fba.allocator()); var string = std.ArrayList(u8).init(fba.allocator());
if (std.json.stringify(value, options, string.writer())) { if (std.json.stringify(value, options, string.writer())) {
return string.items; return string.items;
} else |_| { // error } else |err| { // error
return null; return err;
} }
} }

View file

@ -132,7 +132,7 @@ pub const ContentType = enum {
pub const FioHttpRequestFn = *const fn (r: [*c]fio.http_s) callconv(.C) void; pub const FioHttpRequestFn = *const fn (r: [*c]fio.http_s) callconv(.C) void;
/// Zap Http request callback function type. /// 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 /// websocket connection upgrade callback type
/// fn(request, targetstring) /// fn(request, targetstring)
@ -202,8 +202,10 @@ pub const HttpListener = struct {
req.markAsFinished(false); req.markAsFinished(false);
std.debug.assert(l.settings.on_request != null); std.debug.assert(l.settings.on_request != null);
if (l.settings.on_request) |on_request| { if (l.settings.on_request) |on_request| {
// l.settings.on_request.?(req); on_request(req) catch |err| {
on_request(req); // 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 = .{}; var user_context: Request.UserContext = .{};
req._user_context = &user_context; 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});
};
} }
} }

View file

@ -1,7 +1,7 @@
const std = @import("std"); const std = @import("std");
const zap = @import("zap"); const zap = @import("zap");
fn on_request(r: zap.Request) void { fn on_request(r: zap.Request) !void {
r.setStatus(.not_found); r.setStatus(.not_found);
r.sendBody("<html><body><h1>404 - File not found</h1></body></html>") catch return; r.sendBody("<html><body><h1>404 - File not found</h1></body></html>") catch return;
} }

View file

@ -1,8 +1,8 @@
const std = @import("std"); const std = @import("std");
const zap = @import("zap"); const zap = @import("zap");
fn on_request_minimal(r: zap.Request) void { fn on_request_minimal(r: zap.Request) !void {
r.sendBody("Hello from ZAP!!!") catch return; try r.sendBody("Hello from ZAP!!!");
} }
pub fn main() !void { pub fn main() !void {