mirror of
https://github.com/zigzap/zap.git
synced 2025-10-20 15:14:08 +00:00
222 lines
7 KiB
Zig
222 lines
7 KiB
Zig
const std = @import("std");
|
|
const zap = @import("zap");
|
|
|
|
// just a way to share our allocator via callback
|
|
const SharedAllocator = struct {
|
|
// static
|
|
var allocator: std.mem.Allocator = undefined;
|
|
|
|
// just a convenience function
|
|
pub fn init(a: std.mem.Allocator) void {
|
|
allocator = a;
|
|
}
|
|
|
|
// static function we can pass to the listener later
|
|
pub fn getAllocator() std.mem.Allocator {
|
|
return allocator;
|
|
}
|
|
};
|
|
|
|
// create a combined context struct
|
|
const Context = struct {
|
|
user: ?UserMiddleWare.User = null,
|
|
session: ?SessionMiddleWare.Session = null,
|
|
};
|
|
|
|
// we create a Handler type based on our Context
|
|
const Handler = zap.Middleware.Handler(Context);
|
|
|
|
//
|
|
// ZIG-CEPTION!!!
|
|
//
|
|
// Note how amazing zig is:
|
|
// - we create the "mixed" context based on the both middleware structs
|
|
// - we create the handler based on this context
|
|
// - we create the middleware structs based on the handler
|
|
// - which needs the context
|
|
// - which needs the middleware structs
|
|
// - ZIG-CEPTION!
|
|
|
|
// Example user middleware: puts user info into the context
|
|
const UserMiddleWare = struct {
|
|
handler: Handler,
|
|
|
|
// Just some arbitrary struct we want in the per-request context
|
|
// note: it MUST have all default values!!!
|
|
// note: it MUST have all default values!!!
|
|
// note: it MUST have all default values!!!
|
|
// note: it MUST have all default values!!!
|
|
// This is so that it can be constructed via .{}
|
|
// as we can't expect the listener to know how to initialize our context structs
|
|
const User = struct {
|
|
name: []const u8 = undefined,
|
|
email: []const u8 = undefined,
|
|
};
|
|
|
|
pub fn init(other: ?*Handler) UserMiddleWare {
|
|
return .{
|
|
.handler = Handler.init(onRequest, other),
|
|
};
|
|
}
|
|
|
|
// we need the handler as a common interface to chain stuff
|
|
pub fn getHandler(self: *UserMiddleWare) *Handler {
|
|
return &self.handler;
|
|
}
|
|
|
|
// note that the first parameter is of type *Handler, not *UserMiddleWare !!!
|
|
pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) !bool {
|
|
|
|
// this is how we would get our self pointer
|
|
const self: *UserMiddleWare = @fieldParentPtr("handler", handler);
|
|
_ = self;
|
|
|
|
// do our work: fill in the user field of the context
|
|
context.user = User{
|
|
.name = "renerocksai",
|
|
.email = "supa@secret.org",
|
|
};
|
|
|
|
std.debug.print("\n\nUser Middleware: set user in context {any}\n\n", .{context.user});
|
|
|
|
// continue in the chain
|
|
return handler.handleOther(r, context);
|
|
}
|
|
};
|
|
|
|
// Example session middleware: puts session info into the context
|
|
const SessionMiddleWare = struct {
|
|
handler: Handler,
|
|
|
|
// Just some arbitrary struct we want in the per-request context
|
|
// note: it MUST have all default values!!!
|
|
const Session = struct {
|
|
info: []const u8 = undefined,
|
|
token: []const u8 = undefined,
|
|
};
|
|
|
|
pub fn init(other: ?*Handler) SessionMiddleWare {
|
|
return .{
|
|
.handler = Handler.init(onRequest, other),
|
|
};
|
|
}
|
|
|
|
// we need the handler as a common interface to chain stuff
|
|
pub fn getHandler(self: *SessionMiddleWare) *Handler {
|
|
return &self.handler;
|
|
}
|
|
|
|
// note that the first parameter is of type *Handler, not *SessionMiddleWare !!!
|
|
pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) !bool {
|
|
// this is how we would get our self pointer
|
|
const self: *SessionMiddleWare = @fieldParentPtr("handler", handler);
|
|
_ = self;
|
|
|
|
context.session = Session{
|
|
.info = "secret session",
|
|
.token = "rot47-asdlkfjsaklfdj",
|
|
};
|
|
|
|
std.debug.print("\n\nSessionMiddleware: set session in context {any}\n\n", .{context.session});
|
|
|
|
// continue in the chain
|
|
return handler.handleOther(r, context);
|
|
}
|
|
};
|
|
|
|
// Example html middleware: handles the request and sends a response
|
|
const HtmlMiddleWare = struct {
|
|
handler: Handler,
|
|
|
|
pub fn init(other: ?*Handler) HtmlMiddleWare {
|
|
return .{
|
|
.handler = Handler.init(onRequest, other),
|
|
};
|
|
}
|
|
|
|
// we need the handler as a common interface to chain stuff
|
|
pub fn getHandler(self: *HtmlMiddleWare) *Handler {
|
|
return &self.handler;
|
|
}
|
|
|
|
// note that the first parameter is of type *Handler, not *HtmlMiddleWare !!!
|
|
pub fn onRequest(handler: *Handler, r: zap.Request, context: *Context) !bool {
|
|
|
|
// this is how we would get our self pointer
|
|
const self: *HtmlMiddleWare = @fieldParentPtr("handler", handler);
|
|
_ = self;
|
|
|
|
std.debug.print("\n\nHtmlMiddleware: handling request with context: {any}\n\n", .{context});
|
|
|
|
var buf: [1024]u8 = undefined;
|
|
var userFound: bool = false;
|
|
var sessionFound: bool = false;
|
|
if (context.user) |user| {
|
|
userFound = true;
|
|
if (context.session) |session| {
|
|
sessionFound = true;
|
|
|
|
std.debug.assert(r.isFinished() == false);
|
|
const message = 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);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
const message = std.fmt.bufPrint(&buf, "User info found: {}, session info found: {}", .{ userFound, sessionFound }) catch unreachable;
|
|
|
|
r.setContentType(.TEXT) catch unreachable;
|
|
r.sendBody(message) catch unreachable;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
pub fn main() !void {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{
|
|
.thread_safe = true,
|
|
}){};
|
|
const allocator = gpa.allocator();
|
|
SharedAllocator.init(allocator);
|
|
|
|
// we create our HTML middleware component that handles the request
|
|
var htmlHandler = HtmlMiddleWare.init(null);
|
|
|
|
// we wrap it in the session Middleware component
|
|
var sessionHandler = SessionMiddleWare.init(htmlHandler.getHandler());
|
|
|
|
// we wrap that in the user Middleware component
|
|
var userHandler = UserMiddleWare.init(sessionHandler.getHandler());
|
|
|
|
// we create a listener with our combined context
|
|
// and pass it the initial handler: the user handler
|
|
var listener = try zap.Middleware.Listener(Context).init(
|
|
.{
|
|
.on_request = null, // must be null
|
|
.port = 3000,
|
|
.log = true,
|
|
.max_clients = 100000,
|
|
},
|
|
userHandler.getHandler(),
|
|
SharedAllocator.getAllocator,
|
|
);
|
|
zap.enableDebugLog();
|
|
listener.listen() catch |err| {
|
|
std.debug.print("\nLISTEN ERROR: {any}\n", .{err});
|
|
return;
|
|
};
|
|
|
|
std.debug.print("Visit me on http://127.0.0.1:3000\n", .{});
|
|
|
|
// start worker threads
|
|
zap.start(.{
|
|
.threads = 2,
|
|
.workers = 1,
|
|
});
|
|
}
|