Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9a26ad1

Browse files
committedFeb 9, 2025
feat(middleware): apply middleware per handler instead of per route
1 parent 4ca661f commit 9a26ad1

File tree

9 files changed

+54
-92
lines changed

9 files changed

+54
-92
lines changed
 

‎docs/https.md

-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ pub fn main() !void {
6464
).layer(),
6565
}, .{});
6666
defer router.deinit(allocator);
67-
router.print_route_tree();
6867
6968
// create socket for tardy
7069
var socket = try Socket.init(.{ .tcp = .{ .host = host, .port = port } });

‎examples/fs/main.zig

-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ pub fn main() !void {
5757
FsDir.serve("/", static_dir),
5858
}, .{});
5959
defer router.deinit(allocator);
60-
router.print_route_tree();
6160

6261
const EntryParams = struct {
6362
router: *const Router,

‎examples/middleware/main.zig

-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ pub fn main() !void {
6969
Route.init("/fail").get(num, root_handler).layer(),
7070
}, .{});
7171
defer router.deinit(allocator);
72-
router.print_route_tree();
7372

7473
const EntryParams = struct {
7574
router: *const Router,

‎examples/tls/main.zig

-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ pub fn main() !void {
5959
).layer(),
6060
}, .{});
6161
defer router.deinit(allocator);
62-
router.print_route_tree();
6362

6463
// create socket for tardy
6564
var socket = try Socket.init(.{ .tcp = .{ .host = host, .port = port } });

‎examples/unix/main.zig

-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ pub fn main() !void {
3939
Route.init("/").get({}, root_handler).layer(),
4040
}, .{});
4141
defer router.deinit(allocator);
42-
router.print_route_tree();
4342

4443
const EntryParams = struct {
4544
router: *const Router,

‎src/http/router.zig

+6-12
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const Route = @import("router/route.zig").Route;
77
const TypedHandlerFn = @import("router/route.zig").TypedHandlerFn;
88

99
const Bundle = @import("router/routing_trie.zig").Bundle;
10-
const FoundBundle = @import("router/routing_trie.zig").FoundBundle;
1110

1211
const Capture = @import("router/routing_trie.zig").Capture;
1312
const Request = @import("request.zig").Request;
@@ -59,25 +58,20 @@ pub const Router = struct {
5958
self.routes.deinit(allocator);
6059
}
6160

62-
pub fn print_route_tree(self: *const Router) void {
63-
self.routes.print();
64-
}
65-
6661
pub fn get_bundle_from_host(
6762
self: *const Router,
6863
allocator: std.mem.Allocator,
6964
path: []const u8,
7065
captures: []Capture,
7166
queries: *AnyCaseStringMap,
72-
) !FoundBundle {
67+
) !Bundle {
7368
queries.clearRetainingCapacity();
7469

75-
return try self.routes.get_bundle(allocator, path, captures, queries) orelse {
76-
const not_found_bundle: Bundle = .{
77-
.route = Route.init("").all({}, self.configuration.not_found),
78-
.middlewares = &.{},
79-
};
80-
return .{ .bundle = not_found_bundle, .captures = captures[0..0], .queries = queries, .duped = &.{} };
70+
return try self.routes.get_bundle(allocator, path, captures, queries) orelse Bundle{
71+
.route = Route.init("").all({}, self.configuration.not_found),
72+
.captures = captures[0..],
73+
.queries = queries,
74+
.duped = &.{},
8175
};
8276
}
8377
};

‎src/http/router/route.zig

+28-25
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,26 @@ const FsDir = @import("fs_dir.zig").FsDir;
1616
const Context = @import("../context.zig").Context;
1717
const Layer = @import("middleware.zig").Layer;
1818

19+
const MiddlewareWithData = @import("middleware.zig").MiddlewareWithData;
20+
1921
pub const HandlerFn = *const fn (*const Context, usize) anyerror!Respond;
2022
pub fn TypedHandlerFn(comptime T: type) type {
2123
return *const fn (*const Context, T) anyerror!Respond;
2224
}
2325

2426
pub const HandlerWithData = struct {
2527
handler: HandlerFn,
28+
middlewares: []const MiddlewareWithData,
2629
data: usize,
2730
};
2831

2932
/// Structure of a server route definition.
3033
pub const Route = struct {
31-
const Self = @This();
32-
3334
/// Defined route path.
3435
path: []const u8,
3536

36-
/// Route handlers.
37-
handlers: [9]?HandlerWithData = [_]?HandlerWithData{null} ** 9,
37+
/// Route Handlers.
38+
handlers: [9]?HandlerWithData = .{null} ** 9,
3839

3940
fn method_to_index(method: Method) u32 {
4041
return switch (method) {
@@ -51,13 +52,13 @@ pub const Route = struct {
5152
}
5253

5354
/// Initialize a route for the given path.
54-
pub fn init(path: []const u8) Self {
55-
return Self{ .path = path };
55+
pub fn init(path: []const u8) Route {
56+
return Route{ .path = path };
5657
}
5758

5859
/// Returns a comma delinated list of allowed Methods for this route. This
5960
/// is meant to be used as the value for the 'Allow' header in the Response.
60-
pub fn get_allowed(self: Self, allocator: std.mem.Allocator) ![]const u8 {
61+
pub fn get_allowed(self: Route, allocator: std.mem.Allocator) ![]const u8 {
6162
// This gets allocated within the context of the connection's arena.
6263
const allowed_size = comptime blk: {
6364
var size = 0;
@@ -89,82 +90,84 @@ pub const Route = struct {
8990

9091
/// Get a defined request handler for the provided method.
9192
/// Return NULL if no handler is defined for this method.
92-
pub fn get_handler(self: Self, method: Method) ?HandlerWithData {
93+
pub fn get_handler(self: Route, method: Method) ?HandlerWithData {
9394
return self.handlers[method_to_index(method)];
9495
}
9596

96-
pub fn layer(self: Self) Layer {
97+
pub fn layer(self: Route) Layer {
9798
return .{ .route = self };
9899
}
99100

100101
/// Set a handler function for the provided method.
101102
inline fn inner_route(
102103
comptime method: Method,
103-
self: Self,
104+
self: Route,
104105
data: anytype,
105106
handler_fn: TypedHandlerFn(@TypeOf(data)),
106-
) Self {
107+
) Route {
107108
const wrapped = wrap(usize, data);
108109
var new_handlers = self.handlers;
109110
new_handlers[comptime method_to_index(method)] = .{
110111
.handler = @ptrCast(handler_fn),
112+
.middlewares = &.{},
111113
.data = wrapped,
112114
};
113115

114-
return Self{ .path = self.path, .handlers = new_handlers };
116+
return Route{ .path = self.path, .handlers = new_handlers };
115117
}
116118

117119
/// Set a handler function for all methods.
118-
pub fn all(self: Self, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Self {
120+
pub fn all(self: Route, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Route {
119121
const wrapped = wrap(usize, data);
120122
var new_handlers = self.handlers;
121123

122124
for (&new_handlers) |*new_handler| {
123125
new_handler.* = .{
124126
.handler = @ptrCast(handler_fn),
127+
.middlewares = &.{},
125128
.data = wrapped,
126129
};
127130
}
128131

129-
return Self{
132+
return Route{
130133
.path = self.path,
131134
.handlers = new_handlers,
132135
};
133136
}
134137

135-
pub fn get(self: Self, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Self {
138+
pub fn get(self: Route, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Route {
136139
return inner_route(.GET, self, data, handler_fn);
137140
}
138141

139-
pub fn head(self: Self, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Self {
142+
pub fn head(self: Route, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Route {
140143
return inner_route(.HEAD, self, handler_fn);
141144
}
142145

143-
pub fn post(self: Self, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Self {
146+
pub fn post(self: Route, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Route {
144147
return inner_route(.POST, self, data, handler_fn);
145148
}
146149

147-
pub fn put(self: Self, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Self {
150+
pub fn put(self: Route, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Route {
148151
return inner_route(.PUT, self, data, handler_fn);
149152
}
150153

151-
pub fn delete(self: Self, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Self {
154+
pub fn delete(self: Route, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Route {
152155
return inner_route(.DELETE, self, data, handler_fn);
153156
}
154157

155-
pub fn connect(self: Self, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Self {
158+
pub fn connect(self: Route, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Route {
156159
return inner_route(.CONNECT, self, data, handler_fn);
157160
}
158161

159-
pub fn options(self: Self, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Self {
162+
pub fn options(self: Route, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Route {
160163
return inner_route(.OPTIONS, self, data, handler_fn);
161164
}
162165

163-
pub fn trace(self: Self, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Self {
166+
pub fn trace(self: Route, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Route {
164167
return inner_route(.TRACE, self, data, handler_fn);
165168
}
166169

167-
pub fn patch(self: Self, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Self {
170+
pub fn patch(self: Route, data: anytype, handler_fn: TypedHandlerFn(@TypeOf(data))) Route {
168171
return inner_route(.PATCH, self, data, handler_fn);
169172
}
170173

@@ -177,10 +180,10 @@ pub const Route = struct {
177180

178181
/// Define a GET handler to serve an embedded file.
179182
pub fn embed_file(
180-
self: *const Self,
183+
self: *const Route,
181184
comptime opts: ServeEmbeddedOptions,
182185
comptime bytes: []const u8,
183-
) Self {
186+
) Route {
184187
return self.get({}, struct {
185188
fn handler_fn(ctx: *const Context, _: void) !Respond {
186189
var header_list = try std.ArrayListUnmanaged([2][]const u8).initCapacity(ctx.allocator, 3);

‎src/http/router/routing_trie.zig

+18-48
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,12 @@ const Route = @import("route.zig").Route;
88
const Respond = @import("../response.zig").Respond;
99
const Context = @import("../lib.zig").Context;
1010

11+
const HandlerWithData = @import("route.zig").HandlerWithData;
1112
const MiddlewareWithData = @import("middleware.zig").MiddlewareWithData;
1213
const AnyCaseStringMap = @import("../../core/any_case_string_map.zig").AnyCaseStringMap;
1314

1415
const decode_alloc = @import("../form.zig").decode_alloc;
1516

16-
pub const Bundle = struct {
17-
route: Route,
18-
middlewares: []const MiddlewareWithData,
19-
};
20-
2117
fn TokenHashMap(comptime V: type) type {
2218
return std.HashMap(Token, V, struct {
2319
pub fn hash(self: @This(), input: Token) u64 {
@@ -118,8 +114,8 @@ pub const Capture = union(TokenMatch) {
118114
};
119115

120116
/// Structure of a matched route.
121-
pub const FoundBundle = struct {
122-
bundle: Bundle,
117+
pub const Bundle = struct {
118+
route: Route,
123119
captures: []Capture,
124120
queries: *AnyCaseStringMap,
125121
duped: []const []const u8,
@@ -134,14 +130,14 @@ pub const RoutingTrie = struct {
134130
pub const ChildrenMap = TokenHashMap(Node);
135131

136132
token: Token,
137-
bundle: ?Bundle = null,
133+
route: ?Route = null,
138134
children: ChildrenMap,
139135

140136
/// Initialize a new empty node.
141-
pub fn init(allocator: std.mem.Allocator, token: Token, bundle: ?Bundle) Node {
137+
pub fn init(allocator: std.mem.Allocator, token: Token, route: ?Route) Node {
142138
return .{
143139
.token = token,
144-
.bundle = bundle,
140+
.route = route,
145141
.children = ChildrenMap.init(allocator),
146142
};
147143
}
@@ -183,9 +179,15 @@ pub const RoutingTrie = struct {
183179
}
184180
}
185181

186-
current.bundle = .{
187-
.route = route,
188-
.middlewares = self.middlewares.items,
182+
const r: *Route = if (current.route) |*inner| inner else blk: {
183+
current.route = route;
184+
break :blk &current.route.?;
185+
};
186+
187+
for (route.handlers, 0..) |handler, i| if (handler) |h| {
188+
const slot_ptr: *HandlerWithData = @ptrCast(&r.handlers[i]);
189+
slot_ptr.* = h;
190+
slot_ptr.middlewares = self.middlewares.items;
189191
};
190192
},
191193
.middleware => |mw| try self.middlewares.append(allocator, mw),
@@ -200,45 +202,13 @@ pub const RoutingTrie = struct {
200202
self.middlewares.deinit(allocator);
201203
}
202204

203-
fn print_node(root: *const Node, depth: usize) void {
204-
var i: usize = 0;
205-
while (i < depth) : (i += 1) {
206-
std.debug.print(" │ ", .{});
207-
}
208-
209-
std.debug.print(" ├ ", .{});
210-
211-
switch (root.token) {
212-
.fragment => |inner| std.debug.print("Token: \"{s}\"", .{inner}),
213-
.match => |match| std.debug.print("Token: match {s}", .{@tagName(match)}),
214-
}
215-
216-
if (root.bundle) |bundle| {
217-
std.debug.print(" [x] ({d})", .{bundle.middlewares.len});
218-
} else {
219-
std.debug.print(" [ ]", .{});
220-
}
221-
std.debug.print("\n", .{});
222-
223-
var iter = root.children.valueIterator();
224-
225-
while (iter.next()) |node| {
226-
print_node(node, depth + 1);
227-
}
228-
}
229-
230-
pub fn print(self: *const Self) void {
231-
std.debug.print("Root: \n", .{});
232-
print_node(&self.root, 0);
233-
}
234-
235205
pub fn get_bundle(
236206
self: Self,
237207
allocator: std.mem.Allocator,
238208
path: []const u8,
239209
captures: []Capture,
240210
queries: *AnyCaseStringMap,
241-
) !?FoundBundle {
211+
) !?Bundle {
242212
var capture_idx: usize = 0;
243213
const query_pos = std.mem.indexOfScalar(u8, path, '?');
244214
var iter = std.mem.tokenizeScalar(u8, path[0..(query_pos orelse path.len)], '/');
@@ -321,8 +291,8 @@ pub const RoutingTrie = struct {
321291
}
322292
}
323293

324-
return .{
325-
.bundle = current.bundle orelse return null,
294+
return Bundle{
295+
.route = current.route orelse return null,
326296
.captures = captures[0..capture_idx],
327297
.queries = queries,
328298
.duped = try duped.toOwnedSlice(allocator),

‎src/http/server.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ pub const Server = struct {
369369
defer rt.allocator.free(found.duped);
370370
defer for (found.duped) |dupe| rt.allocator.free(dupe);
371371

372-
const h_with_data: HandlerWithData = found.bundle.route.get_handler(
372+
const h_with_data: HandlerWithData = found.route.get_handler(
373373
provision.request.method.?,
374374
) orelse {
375375
try provision.response.apply(.{
@@ -395,7 +395,7 @@ pub const Server = struct {
395395

396396
var next: Next = .{
397397
.context = &context,
398-
.middlewares = found.bundle.middlewares,
398+
.middlewares = h_with_data.middlewares,
399399
.handler = h_with_data,
400400
};
401401

0 commit comments

Comments
 (0)
Please sign in to comment.