Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simple router with handler clojures support #68

Merged
merged 3 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub fn build(b: *std.build.Builder) !void {
.{ .name = "hello", .src = "examples/hello/hello.zig" },
.{ .name = "https", .src = "examples/https/https.zig" },
.{ .name = "hello2", .src = "examples/hello2/hello2.zig" },
.{ .name = "hello3", .src = "examples/hello3/hello3.zig" },
.{ .name = "routes", .src = "examples/routes/routes.zig" },
.{ .name = "serve", .src = "examples/serve/serve.zig" },
.{ .name = "hello_json", .src = "examples/hello_json/hello_json.zig" },
Expand Down
108 changes: 108 additions & 0 deletions examples/hello3/hello3.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const std = @import("std");
const zap = @import("zap");
const Allocator = std.mem.Allocator;

fn on_request_verbose(r: zap.Request) void {
if (r.path) |the_path| {
std.debug.print("PATH: {s}\n", .{the_path});
}

if (r.query) |the_query| {
std.debug.print("QUERY: {s}\n", .{the_query});
}
r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
}

pub const SomePackage = struct {
const Self = @This();

allocator: Allocator,
a: i8,
b: i8,

pub fn init(allocator: Allocator, a: i8, b: i8) Self {
return .{
.allocator = allocator,
.a = a,
.b = b,
};
}

pub fn getA(self: *Self, req: zap.Request) void {
std.log.warn("get_a_requested", .{});

const string = std.fmt.allocPrint(
self.allocator,
"A value is {d}\n",
.{self.a},
) catch return;
defer self.allocator.free(string);

req.sendBody(string) catch return;
}

pub fn getB(self: *Self, req: zap.Request) void {
std.log.warn("get_b_requested", .{});

const string = std.fmt.allocPrint(
self.allocator,
"B value is {d}\n",
.{self.b},
) catch return;
defer self.allocator.free(string);

req.sendBody(string) catch return;
}

pub fn incrementA(self: *Self, req: zap.Request) void {
std.log.warn("increment_a_requested", .{});

self.a += 1;

req.sendBody("incremented A") catch return;
}
};

fn not_found(req: zap.Request) void {
std.debug.print("not found handler", .{});

req.sendBody("Not found") catch return;
}

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{
.thread_safe = true,
}){};
var allocator = gpa.allocator();

var simpleRouter = zap.Router.init(allocator, .{
.not_found = not_found,
});
defer simpleRouter.deinit();

var somePackage = SomePackage.init(allocator, 1, 2);

try simpleRouter.handle_func("/", on_request_verbose);

try simpleRouter.handle_func("/geta", zap.RequestHandler(&somePackage, SomePackage.getA));

try simpleRouter.handle_func("/getb", zap.RequestHandler(&somePackage, SomePackage.getB));

try simpleRouter.handle_func("/inca", zap.RequestHandler(&somePackage, SomePackage.incrementA));

var listener = zap.HttpListener.init(.{
.port = 3000,
.on_request = zap.RequestHandler(&simpleRouter, &zap.Router.serve),
StringNick marked this conversation as resolved.
Show resolved Hide resolved
.log = true,
.max_clients = 100000,
});
try listener.listen();

std.debug.print("Listening on 0.0.0.0:3000\n", .{});

// start worker threads
zap.start(.{
.threads = 2,
.workers = 2,
});
}
77 changes: 77 additions & 0 deletions src/router.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const std = @import("std");
const zap = @import("zap.zig");

const Allocator = std.mem.Allocator;
const RouterError = error{
AlreadyExists,
EmptyPath,
};

// inline closure for RequestFn with self argument
pub inline fn RequestHandler(self: anytype, func: *const fn (@TypeOf(self), zap.Request) void) *const fn (zap.Request) void {
StringNick marked this conversation as resolved.
Show resolved Hide resolved
return (opaque {
var hidden_self: @TypeOf(self) = undefined;
var hidden_func: *const fn (@TypeOf(self), zap.Request) void = undefined;
pub fn init(h_self: @TypeOf(self), h_func: *const fn (@TypeOf(self), zap.Request) void) *const @TypeOf(run) {
hidden_self = h_self;
hidden_func = h_func;
return &run;
}

fn run(req: zap.Request) void {
hidden_func(hidden_self, req);
}
}).init(self, func);
}

pub const RouterOptions = struct {
StringNick marked this conversation as resolved.
Show resolved Hide resolved
not_found: ?zap.HttpRequestFn = null,
};

pub const Router = struct {
StringNick marked this conversation as resolved.
Show resolved Hide resolved
const Self = @This();

routes: std.StringHashMap(zap.HttpRequestFn),
not_found: ?zap.HttpRequestFn,

pub fn init(allocator: Allocator, options: RouterOptions) Self {
return .{
.routes = std.StringHashMap(zap.HttpRequestFn).init(allocator),

.not_found = options.not_found,
};
}

pub fn deinit(self: *Self) void {
self.routes.deinit();
}

pub fn handle_func(self: *Self, path: []const u8, h: zap.HttpRequestFn) !void {
if (path.len == 0) {
return RouterError.EmptyPath;
}

const route = self.routes.get(path);

if (route != null) {
return RouterError.AlreadyExists;
}

try self.routes.put(path, h);
}

pub fn serve(self: *Self, r: zap.Request) void {
var route = self.routes.get(r.path.?);
StringNick marked this conversation as resolved.
Show resolved Hide resolved

if (route) |handler| {
handler(r);
} else if (self.not_found) |handler| {
// not found handler
handler(r);
} else {
// default 404 output
r.setStatus(.not_found);
r.sendBody("404 Not Found") catch return;
}
}
};
2 changes: 2 additions & 0 deletions src/zap.zig
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ pub const Tls = @import("tls.zig");
/// ```
pub const Endpoint = @import("endpoint.zig");

pub usingnamespace @import("router.zig");
StringNick marked this conversation as resolved.
Show resolved Hide resolved

pub usingnamespace @import("util.zig");
pub usingnamespace @import("http.zig");

Expand Down