From 0736c8a26dbd970670117290baf4a68e430aecaf Mon Sep 17 00:00:00 2001 From: Martin Ashby Date: Tue, 22 Aug 2023 09:04:23 +0100 Subject: Decouple Router from std.http module In case we want to use it in a CGI application :^) --- src/zigwebserver.zig | 58 +++++++++++++++++++++------------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/src/zigwebserver.zig b/src/zigwebserver.zig index 2e20382..acf65fb 100644 --- a/src/zigwebserver.zig +++ b/src/zigwebserver.zig @@ -64,8 +64,6 @@ pub fn Server(comptime Context: type, comptime Handler: type) type { }; } -const Response = std.http.Server.Response; - pub const Params = std.StringHashMap([]const u8); /// Routing component for an http server with wildcard matching and parameter @@ -80,24 +78,22 @@ pub const Params = std.StringHashMap([]const u8); /// "/*" -> matches all requests /// TODO something clever to parse path parameters into the appropriate types, maybe smth like "/foo/{bar:u32}/baz" /// TODO something to handle query parameters and request body too -pub fn Router(comptime Context: type, comptime ErrorType: type) type { +pub fn Router(comptime Response: type, comptime Context: type, comptime ErrorType: type) type { return struct { pub const Handler = struct { method: std.http.Method, pattern: []const u8, - handle_fn: *const fn (res: *Response, ctx: Context, Params) ErrorType!void, + handle_fn: *const fn (Response, Context, Params) ErrorType!void, }; - handlers: []const Handler, + allocator: std.mem.Allocator, - notfound: *const fn (res: *Response, ctx: Context) ErrorType!void, + handlers: []const Handler, - pub fn handle(self: @This(), res: *Response, ctx: Context) ErrorType!void { - // Routing can only happen after we have the headers - // It is a programmer error to call this without calling .wait first. - if (res.state != .waited) unreachable; + notfound: *const fn (Response, Context) ErrorType!void, - var p = try Path.parse(res.allocator, res.request.target); + pub fn handle(self: @This(), res: Response, ctx: Context) ErrorType!void { + var p = try Path.parse(self.allocator, res.request.target); defer p.deinit(); const path = p.path; @@ -106,7 +102,7 @@ pub fn Router(comptime Context: type, comptime ErrorType: type) type { continue :handler_loop; } - var path_params: Params = std.StringHashMap([]const u8).init(res.allocator); + var path_params: Params = std.StringHashMap([]const u8).init(self.allocator); defer path_params.deinit(); var handle_split = std.mem.splitScalar(u8, handler.pattern, '/'); @@ -150,22 +146,29 @@ pub fn Router(comptime Context: type, comptime ErrorType: type) type { } const RouterTest = struct { + const TestRequest = struct { + method: std.http.Method, + target: []const u8, + }; + const TestResponse = struct { + request: TestRequest, + }; const TestCtx = struct {}; const TestErr = error{ TestError, OutOfMemory } || Path.ParseError; - const TestRouter = Router(TestCtx, TestErr); + const TestRouter = Router(TestResponse, TestCtx, TestErr); var notfoundinvoked = false; - fn notfound(_: *Response, _: TestCtx) TestErr!void { + fn notfound(_: TestResponse, _: TestCtx) TestErr!void { notfoundinvoked = true; } var route1invoked = false; var route1params: ?Params = null; - fn route1(_: *Response, _: TestCtx, p: Params) TestErr!void { + fn route1(_: TestResponse, _: TestCtx, p: Params) TestErr!void { route1invoked = true; route1params = try p.clone(); } var route2invoked = false; - fn route2(_: *Response, _: TestCtx, _: Params) TestErr!void { + fn route2(_: TestResponse, _: TestCtx, _: Params) TestErr!void { route2invoked = true; } fn reset() void { @@ -177,35 +180,20 @@ const RouterTest = struct { } fn runTestRouter(handlers: []TestRouter.Handler, target: []const u8) !void { - const alloc = std.testing.allocator; const ctx = TestCtx{}; - var buf: [128]u8 = undefined; - const req = std.http.Server.Request{ + const req = TestRequest{ .method = .GET, .target = target, - .version = .@"HTTP/1.1", - .headers = std.http.Headers.init(alloc), - .parser = std.http.protocol.HeadersParser.initStatic(&buf), - }; - const sock = try std.net.tcpConnectToAddress(std.net.Address{ .in = std.net.Ip4Address.init(.{ 127, 0, 0, 1 }, 22) }); - defer sock.close(); - const conn = std.http.Server.Connection{ - .stream = sock, - .protocol = .plain, }; - var res = Response{ - .allocator = alloc, - .address = std.net.Address{ .in = std.net.Ip4Address.init(.{ 127, 0, 0, 1 }, 8080) }, - .connection = conn, - .headers = std.http.Headers.init(alloc), + var res = TestResponse{ .request = req, - .state = .waited, }; const router = TestRouter{ + .allocator = std.testing.allocator, .handlers = handlers, .notfound = notfound, }; - try router.handle(&res, ctx); + try router.handle(res, ctx); } // fn hmof(x: []const u8, y: []const u8) std.StringHashMap([]const u8) { -- cgit v1.2.3-ZIG