From 29f12b67794dcb4f48440a0450ad5682151b8e9d Mon Sep 17 00:00:00 2001 From: Jeeves Date: Wed, 9 Oct 2024 13:06:01 -0600 Subject: [PATCH 1/2] update to zig master and give up --- .gitignore | 2 +- build.zig | 4 +-- flake.lock | 78 +++++++++++++++++++++++++++++++++++++++++++++ src/application.zig | 22 +++++++------ src/main.zig | 16 ++++++---- 5 files changed, 103 insertions(+), 19 deletions(-) create mode 100644 flake.lock diff --git a/.gitignore b/.gitignore index feda423..8ee9b4c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ # Cheers! # -andrewrk -zig-cache/ +.zig-cache/ zig-out/ /release/ /debug/ diff --git a/build.zig b/build.zig index 37a2704..728c5ec 100644 --- a/build.zig +++ b/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "master", - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); @@ -21,7 +21,7 @@ pub fn build(b: *std.Build) void { run_step.dependOn(&run_cmd.step); const exe_unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..95d4f17 --- /dev/null +++ b/flake.lock @@ -0,0 +1,78 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1726108120, + "narHash": "sha256-Ji5wO1lLG99grI0qCRb6FyRPpH9tfdfD1QP/r7IlgfM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "111ed8812c10d7dc3017de46cbf509600c93f551", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "zig2nix": "zig2nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "zig2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1728437366, + "narHash": "sha256-zZF5drwqCC41UpQddNKC6nhAmE2DcON61th+xf1k6n8=", + "owner": "Cloudef", + "repo": "zig2nix", + "rev": "8231ceb8258475093336cac0e8706baf0d634037", + "type": "github" + }, + "original": { + "owner": "Cloudef", + "repo": "zig2nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/src/application.zig b/src/application.zig index 795f706..255ebc6 100644 --- a/src/application.zig +++ b/src/application.zig @@ -62,7 +62,7 @@ pub fn Application(comptime Context: type) type { var event = Event{ .ctx = &ctx, .req = .{ - .uri = try std.Uri.parseWithoutScheme(req.head.target), + .uri = try std.Uri.parseAfterScheme("http://", req.head.target), .headers = std.ArrayList(*const http.Header).init(allocator), }, .res = .{ @@ -126,7 +126,7 @@ pub fn Router(comptime Context: type) type { root_node: *Node, // static_routes: std.StringHashMap(*Node), - pub fn init(allocator: mem.Allocator, root_ctx: Context) Router { + pub fn init(allocator: mem.Allocator, root_ctx: Context) Self { var arena = heap.ArenaAllocator.init(allocator); var node = Node.init(arena.allocator()) catch @panic("OOM"); node.data = root_ctx; @@ -139,19 +139,19 @@ pub fn Router(comptime Context: type) type { }; } - pub fn deinit(self: *Router) void { + pub fn deinit(self: *Self) void { self.root_node.deinit(self.arena.allocator(), null); self.arena.deinit(); // self.static_routes.deinit(); } - // pub fn handle(self: *Router, event: *Listener.Event) !void { - // const route = try self.getRoute(event.req.uri.path); - // try route.handler(event); - // } + pub fn handle(self: *Router, ctx: *Context) !void { + const route = try self.getRoute(ctx.req.uri.path); + try route.handler(ctx); + } /// Insert a route if the path is not already present, otherwise overwrite preexisting HandlerFn. - pub fn putRoute(self: *Router, path: []const u8, ctx: Context) !void { + pub fn putRoute(self: *Self, path: []const u8, ctx: Context) !void { std.debug.print("\npath {s}\n", .{path}); // var is_static_route = true; var sections = mem.splitScalar(u8, path, '/'); @@ -216,7 +216,7 @@ pub fn Router(comptime Context: type) type { } /// Get the HandlerFn associated with path, otherwise get root_handler. - pub fn getRoute(self: *Router, path: []const u8) !*Node { + pub fn getRoute(self: *Self, path: []const u8) !*Node { // if (self.static_routes.get(path)) |rt| return rt; std.debug.print("\nget path {s}\n", .{path}); @@ -277,7 +277,7 @@ pub fn Router(comptime Context: type) type { } /// If there is a route with a matching path, it is deleted from the router, and this function return true. Otherwise it returns false. - pub fn removeRoute(self: *Router, path: []const u8) bool { + pub fn removeRoute(self: *Self, path: []const u8) bool { // _ = self.static_routes.remove(path); std.debug.print("\nremoving path {s}\n", .{path}); @@ -351,6 +351,8 @@ pub fn Router(comptime Context: type) type { allocator.destroy(self); } }; + + const Self = @This(); }; } diff --git a/src/main.zig b/src/main.zig index b1267af..d95621b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -11,23 +11,27 @@ pub fn main() !void { defer _ = gpa.deinit(); const allocator = gpa.allocator(); + var router = Router.init(allocator, handleError); + defer router.deinit(); + + try router.putRoute("/", &handle); + // try listener.router.putRoute("/error", &handleError); + // try listener.router.putRoute("//pee", &handleError); + // try listener.router.putRoute("/error/*", &handleError); + const address = try net.Address.parseIp("0.0.0.0", 8080); var listener = App.init(.{ .address = address, .allocator = allocator, - .root_handler = &handleError, + // .root_handler = , }); defer listener.deinit(); - // try listener.router.putRoute("/", &handle); - // try listener.router.putRoute("/error", &handleError); - // try listener.router.putRoute("//pee", &handleError); - // try listener.router.putRoute("/error/*", &handleError); - try listener.listen(); } const App = Application(DummyContext); +const Router = @import("application.zig").Router(App.HandlerFn); const DummyContext = struct {}; fn handle(event: *App.Event) anyerror!void { From 6340cb0cbedeee0e771801568510532f164f31ba Mon Sep 17 00:00:00 2001 From: Jeeves Date: Mon, 3 Mar 2025 18:36:50 -0700 Subject: [PATCH 2/2] idk --- build.zig | 2 +- src/main.zig | 47 +++++++++--------- src/root.zig | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 src/root.zig diff --git a/build.zig b/build.zig index 728c5ec..252b74f 100644 --- a/build.zig +++ b/build.zig @@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); const exe = b.addExecutable(.{ - .name = "master", + .name = "httz", .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, diff --git a/src/main.zig b/src/main.zig index d95621b..5ede761 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,43 +1,44 @@ const std = @import("std"); -const mem = std.mem; const net = std.net; -const http = std.http; -const heap = std.heap; -pub const Application = @import("application.zig").Application; +const Listener = @import("root.zig").Listener(Context); +const Request = Listener.Request; +const Response = Listener.Response; + +const Context = struct {}; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); - var router = Router.init(allocator, handleError); - defer router.deinit(); - - try router.putRoute("/", &handle); - // try listener.router.putRoute("/error", &handleError); - // try listener.router.putRoute("//pee", &handleError); - // try listener.router.putRoute("/error/*", &handleError); - - const address = try net.Address.parseIp("0.0.0.0", 8080); - var listener = App.init(.{ + const address = net.Address.initIp4(.{ 127, 0, 0, 1 }, 8080); + const listener = try Listener.init(.{ .address = address, .allocator = allocator, - // .root_handler = , + .handler = handle, + .errorHandler = handleError, }); defer listener.deinit(); + try std.io.getStdErr().writer().print("listening at {}\n", .{address}); try listener.listen(); } -const App = Application(DummyContext); -const Router = @import("application.zig").Router(App.HandlerFn); -const DummyContext = struct {}; - -fn handle(event: *App.Event) anyerror!void { - try event.res.body.appendSlice("hello there"); +fn handle(req: *const Request) anyerror!Response { + if (std.mem.eql(u8, req.uri.path.percent_encoded, "/")) { + return .{ .body = "home!" }; + } else { + return .{ .body = "somewhere else!" }; + } } -fn handleError(event: *App.Event) anyerror!void { - try event.res.body.appendSlice("ahoy, an error occurred"); +fn handleError(_: *const Request, err: [:0]const u8) anyerror!Response { + return .{ + .body = "oops! something went wrong on the server side.", + .options = .{ + .status = .internal_server_error, + .reason = err, + }, + }; } diff --git a/src/root.zig b/src/root.zig new file mode 100644 index 0000000..ad8b365 --- /dev/null +++ b/src/root.zig @@ -0,0 +1,137 @@ +const std = @import("std"); +const mem = std.mem; +const net = std.net; +// const http = std.http; + +pub fn Listener(comptime Context: type) type { + _ = Context; + return struct { + // ctx: *Context, + server: *net.Server, + allocator: mem.Allocator, + handler: HandlerFn, + errorHandler: ErrorHandlerFn, + + pub const InitOptions = struct { + address: net.Address, + allocator: mem.Allocator, + listen_options: net.Address.ListenOptions = .{ .reuse_address = true }, + handler: HandlerFn, + errorHandler: ErrorHandlerFn, + }; + + pub fn init(options: InitOptions) !Self { + var server = try options.address.listen(options.listen_options); + return .{ + // .ctx = options.ctx, + .server = &server, + .allocator = options.allocator, + .handler = options.handler, + .errorHandler = options.errorHandler, + }; + } + + pub fn deinit(self: Self) void { + self.server.deinit(); + } + + pub fn listen(self: Self) !void { + while (true) { + const read_buf = try self.allocator.alloc(u8, 1024 * 32); + defer self.allocator.free(read_buf); + + const connection = try self.server.accept(); + defer connection.stream.close(); + + var http = std.http.Server.init(connection, read_buf); + http: while (true) { + var req = http.receiveHead() catch |e| if (e == error.HttpConnectionClosing) break :http else return e; + const request = Request{ + .uri = try std.Uri.parseAfterScheme("http://", req.head.target), + .http = &req, + }; + const res: Response = self.handler(&request) catch |e| blk: { + break :blk self.errorHandler(&request, @errorName(e)) catch |err| { + try req.respond("ahoy, an internal error occurred.", .{ + .status = .internal_server_error, + .reason = @errorName(err), + }); + continue :http; + }; + }; + try req.respond(res.body, res.options); + } + } + } + + pub const Request = struct { + uri: std.Uri, + http: *std.http.Server.Request, + }; + + pub const Response = struct { + options: std.http.Server.Request.RespondOptions = .{}, + body: []const u8, + }; + + const Self = @This(); + + pub const HandlerFn = *const fn (req: *const Request) anyerror!Response; + pub const ErrorHandlerFn = *const fn (req: *const Request, err: [:0]const u8) anyerror!Response; + + // pub const Router = struct { + // static_routes: std.StringHashMap(HandlerFn), + + // fallback_handler: HandlerFn, + // error_handler: HandlerFn, + + // allocator: mem.Allocator, + + // pub const Options = struct { + // allocator: mem.Allocator, + // fallback_handler: HandlerFn, + // error_handler: HandlerFn, + // }; + + // pub fn init(options: Options) Router { + // return .{ + // .allocator = options.allocator, + // .fallback_handler = options.fallback_handler, + // .error_handler = options.error_handler, + // .static_routes = std.StringHashMap(HandlerFn).init(options.allocator), + // }; + // } + + // pub fn deinit(self: Router) void { + // self.static_routes.deinit(); + // } + + // pub fn get(self: Router, path: []const u8) HandlerFn { + // if (self.static_routes.get(path)) |handler_fn| + // return handler_fn + // else + // return self.fallback_handler; + // } + + // pub fn put(self: Router, path: []const u8, handler_fn: HandlerFn) !void { + // if (mem.indexOfAny(u8, path, "*{}") != null) { + // return error.UnsupportedRoute; + // } else try self.static_routes.put(path, handler_fn); + // } + + // pub fn remove(self: Router, path: []const u8) void { + // if (mem.indexOfAny(u8, path, "*{}") != null) { + // return; + // } else _ = self.static_routes.remove(path); + // } + + // pub fn handler(req: Request, ctx: Router) Response { + // const uri = std.Uri.parseAfterScheme("http://", req.http.head.target); + // if (ctx.get(uri.path.percent_encoded)) |handler_fn| + // return handler_fn(req) + // else + // return ctx.fallback_handler(req); + // } + // }; + }; +}