summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Ashby <martin@ashbysoft.com>2023-08-22 09:04:23 +0100
committerMartin Ashby <martin@ashbysoft.com>2023-08-22 09:04:23 +0100
commit0736c8a26dbd970670117290baf4a68e430aecaf (patch)
tree3bbe24f32933699c6a93d5d8cd35e580884c32c3
parent17d8cc65fe6396d505dda6bf162942cab9e00408 (diff)
downloadzigwebserver-0736c8a26dbd970670117290baf4a68e430aecaf.tar.gz
zigwebserver-0736c8a26dbd970670117290baf4a68e430aecaf.tar.bz2
zigwebserver-0736c8a26dbd970670117290baf4a68e430aecaf.tar.xz
zigwebserver-0736c8a26dbd970670117290baf4a68e430aecaf.zip
Decouple Router from std.http module
In case we want to use it in a CGI application :^)
-rw-r--r--src/zigwebserver.zig58
1 files 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) {