1
0
Fork 0
mirror of https://github.com/zigzap/zap.git synced 2025-10-21 07:34:08 +00:00

Merge pull request #68 from StringNick/simple-router

simple router with handler clojures support
This commit is contained in:
Rene Schallner 2024-01-24 13:50:58 +01:00 committed by GitHub
commit 9122d7f9b0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 191 additions and 1 deletions

View file

@ -128,7 +128,8 @@ what's necessary to show a feature.
- run it like this: `ZAP_USE_OPENSSL=true zig build run-https` - run it like this: `ZAP_USE_OPENSSL=true zig build run-https`
OR like this: `zig build -Dopenssl=true run-https` OR like this: `zig build -Dopenssl=true run-https`
- it will tell you how to generate certificates - it will tell you how to generate certificates
- [**Router support**](examples/simple_router/simple_router.zig): You can now use
simple router by path. Also example with self capture helper func.
I'll continue wrapping more of facil.io's functionality and adding stuff to zap I'll continue wrapping more of facil.io's functionality and adding stuff to zap
to a point where I can use it as the JSON REST API backend for real research to a point where I can use it as the JSON REST API backend for real research
projects, serving thousands of concurrent clients. projects, serving thousands of concurrent clients.

View file

@ -34,6 +34,7 @@ pub fn build(b: *std.build.Builder) !void {
.{ .name = "hello", .src = "examples/hello/hello.zig" }, .{ .name = "hello", .src = "examples/hello/hello.zig" },
.{ .name = "https", .src = "examples/https/https.zig" }, .{ .name = "https", .src = "examples/https/https.zig" },
.{ .name = "hello2", .src = "examples/hello2/hello2.zig" }, .{ .name = "hello2", .src = "examples/hello2/hello2.zig" },
.{ .name = "simple_router", .src = "examples/simple_router/simple_router.zig" },
.{ .name = "routes", .src = "examples/routes/routes.zig" }, .{ .name = "routes", .src = "examples/routes/routes.zig" },
.{ .name = "serve", .src = "examples/serve/serve.zig" }, .{ .name = "serve", .src = "examples/serve/serve.zig" },
.{ .name = "hello_json", .src = "examples/hello_json/hello_json.zig" }, .{ .name = "hello_json", .src = "examples/hello_json/hello_json.zig" },

View file

@ -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),
.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,
});
}

60
src/router.zig Normal file
View file

@ -0,0 +1,60 @@
const std = @import("std");
const zap = @import("zap.zig");
const Allocator = std.mem.Allocator;
const RouterError = error{
AlreadyExists,
EmptyPath,
};
const Self = @This();
pub const Options = struct {
not_found: ?zap.HttpRequestFn = null,
};
routes: std.StringHashMap(zap.HttpRequestFn),
not_found: ?zap.HttpRequestFn,
pub fn init(allocator: Allocator, options: Options) 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 {
const path = if (r.path) |p| p else "/";
var route = self.routes.get(path);
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;
}
}

View file

@ -1,5 +1,23 @@
const std = @import("std"); const std = @import("std");
const fio = @import("fio.zig"); const fio = @import("fio.zig");
const zap = @import("zap.zig");
/// capture self for RequestFn signature support
pub inline fn RequestHandler(self: anytype, func: *const fn (@TypeOf(self), zap.Request) void) *const fn (zap.Request) void {
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);
}
/// Used internally: convert a FIO object into its string representation. /// Used internally: convert a FIO object into its string representation.
/// note: since this is called from within request functions, we don't make /// note: since this is called from within request functions, we don't make

View file

@ -52,6 +52,8 @@ pub const Tls = @import("tls.zig");
/// ``` /// ```
pub const Endpoint = @import("endpoint.zig"); pub const Endpoint = @import("endpoint.zig");
pub const Router = @import("router.zig");
pub usingnamespace @import("util.zig"); pub usingnamespace @import("util.zig");
pub usingnamespace @import("http.zig"); pub usingnamespace @import("http.zig");