diff --git a/.gitignore b/.gitignore index 8ee9b4c..feda423 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 252b74f..37a2704 100644 --- a/build.zig +++ b/build.zig @@ -5,8 +5,8 @@ pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); const exe = b.addExecutable(.{ - .name = "httz", - .root_source_file = b.path("src/main.zig"), + .name = "master", + .root_source_file = .{ .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 = b.path("src/main.zig"), + .root_source_file = .{ .path = "src/main.zig" }, .target = target, .optimize = optimize, }); diff --git a/flake.lock b/flake.lock deleted file mode 100644 index 95d4f17..0000000 --- a/flake.lock +++ /dev/null @@ -1,78 +0,0 @@ -{ - "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 255ebc6..795f706 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.parseAfterScheme("http://", req.head.target), + .uri = try std.Uri.parseWithoutScheme(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) Self { + pub fn init(allocator: mem.Allocator, root_ctx: Context) Router { 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: *Self) void { + pub fn deinit(self: *Router) void { self.root_node.deinit(self.arena.allocator(), null); self.arena.deinit(); // self.static_routes.deinit(); } - pub fn handle(self: *Router, ctx: *Context) !void { - const route = try self.getRoute(ctx.req.uri.path); - try route.handler(ctx); - } + // pub fn handle(self: *Router, event: *Listener.Event) !void { + // const route = try self.getRoute(event.req.uri.path); + // try route.handler(event); + // } /// Insert a route if the path is not already present, otherwise overwrite preexisting HandlerFn. - pub fn putRoute(self: *Self, path: []const u8, ctx: Context) !void { + pub fn putRoute(self: *Router, 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: *Self, path: []const u8) !*Node { + pub fn getRoute(self: *Router, 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: *Self, path: []const u8) bool { + pub fn removeRoute(self: *Router, path: []const u8) bool { // _ = self.static_routes.remove(path); std.debug.print("\nremoving path {s}\n", .{path}); @@ -351,8 +351,6 @@ pub fn Router(comptime Context: type) type { allocator.destroy(self); } }; - - const Self = @This(); }; } diff --git a/src/main.zig b/src/main.zig index 5ede761..b1267af 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,44 +1,39 @@ const std = @import("std"); +const mem = std.mem; const net = std.net; +const http = std.http; +const heap = std.heap; -const Listener = @import("root.zig").Listener(Context); -const Request = Listener.Request; -const Response = Listener.Response; - -const Context = struct {}; +pub const Application = @import("application.zig").Application; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); - const address = net.Address.initIp4(.{ 127, 0, 0, 1 }, 8080); - const listener = try Listener.init(.{ + const address = try net.Address.parseIp("0.0.0.0", 8080); + var listener = App.init(.{ .address = address, .allocator = allocator, - .handler = handle, - .errorHandler = handleError, + .root_handler = &handleError, }); defer listener.deinit(); - try std.io.getStdErr().writer().print("listening at {}\n", .{address}); + // 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(); } -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!" }; - } +const App = Application(DummyContext); +const DummyContext = struct {}; + +fn handle(event: *App.Event) anyerror!void { + try event.res.body.appendSlice("hello there"); } -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, - }, - }; +fn handleError(event: *App.Event) anyerror!void { + try event.res.body.appendSlice("ahoy, an error occurred"); } diff --git a/src/root.zig b/src/root.zig deleted file mode 100644 index ad8b365..0000000 --- a/src/root.zig +++ /dev/null @@ -1,137 +0,0 @@ -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); - // } - // }; - }; -}