diff --git a/README.md b/README.md index bfbdf9d..1a76f67 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Straightforward HTTP-like request routing. --- -Project is tested against zig 0.12.0-dev.3496+a2df84d0f +Project is tested against zig 0.13.0-dev.75+5c9eb4081 ## Sample diff --git a/example/main.zig b/example/main.zig index efbd500..9c8570b 100644 --- a/example/main.zig +++ b/example/main.zig @@ -67,7 +67,7 @@ fn getError() !Response { return error.EPIC_FAIL; } -fn onRequest(arena: *std.heap.ArenaAllocator, response: *std.http.Server.Response) !void { +fn onRequest(arena: *std.heap.ArenaAllocator, request: *std.http.Server.Request) !void { defer { const builtin = @import("builtin"); if (builtin.mode == .Debug) { @@ -82,7 +82,7 @@ fn onRequest(arena: *std.heap.ArenaAllocator, response: *std.http.Server.Respons } } - var target_it = std.mem.splitSequence(u8, response.request.target, "?"); + var target_it = std.mem.splitSequence(u8, request.head.target, "?"); const path = std.mem.trimRight(u8, target_it.first(), "/"); const res = router.Router(.{ @@ -95,29 +95,35 @@ fn onRequest(arena: *std.heap.ArenaAllocator, response: *std.http.Server.Respons router.Route(.GET, "/query", getQuery, .{}), router.Route(.GET, "/error", getError, .{}), }).match(arena.allocator(), .{ - .method = response.request.method, + .method = request.head.method, .path = if (path.len > 0) path else "/", .query = target_it.rest(), - .body = .{ .reader = response.reader().any() }, - }, .{ arena.allocator(), response }) catch |err| switch (err) { + .body = .{ .reader = try request.reader() }, + }, .{ arena.allocator() }) catch |err| switch (err) { error.not_found => { - response.status = .not_found; - response.send() catch {}; - response.writeAll("404 Not Found") catch {}; + request.respond("404 Not Found", .{ + .status = .not_found, + .transfer_encoding = .none, + .keep_alive = false, + }) catch {}; return; }, error.bad_request => { - response.status = .bad_request; - response.send() catch {}; - response.writeAll("400 Bad Request") catch {}; + request.respond("404 Bad Request", .{ + .status = .bad_request, + .transfer_encoding = .none, + .keep_alive = false, + }) catch {}; return; }, else => return err, }; - response.status = .ok; - response.send() catch {}; - if (res.body) |body| response.writeAll(body) catch {}; + try request.respond(res.body orelse "", .{ + .status = .ok, + .transfer_encoding = .chunked, + .keep_alive = false, + }); } pub fn main() !void { @@ -125,5 +131,5 @@ pub fn main() !void { defer std.debug.assert(gpa.deinit() == .ok); var arena = std.heap.ArenaAllocator.init(gpa.allocator()); defer arena.deinit(); - try server.run(gpa.allocator(), "127.0.0.1", 8000, onRequest, .{&arena}); + try server.run("127.0.0.1", 8000, onRequest, .{&arena}); } diff --git a/example/server.zig b/example/server.zig index ea07025..9e09b5a 100644 --- a/example/server.zig +++ b/example/server.zig @@ -30,49 +30,44 @@ inline fn Callable(comptime handler: anytype) CallableType(handler) { return .{}; } -fn serve(allocator: std.mem.Allocator, server_addr: []const u8, server_port: u16, context: anytype) !void { - var server = std.http.Server.init(.{ .reuse_address = true }); - defer server.deinit(); - - const address = std.net.Address.parseIp(server_addr, server_port) catch unreachable; - try server.listen(address); +fn serve(server_addr: []const u8, server_port: u16, context: anytype) !void { + const address = try std.net.Address.parseIp(server_addr, server_port); + var tcp_server = try address.listen(.{ + .reuse_address = true, + .reuse_port = true, + }); + defer tcp_server.deinit(); log.info("Server is running at {s}:{d}", .{ server_addr, server_port }); - outer: while (true) { - var response = try server.accept(.{ .allocator = allocator }); - defer response.deinit(); - - while (response.reset() != .closing) { - response.wait() catch |err| switch (err) { - error.HttpHeadersInvalid => continue :outer, - error.EndOfStream => continue, - else => return err, - }; + accept: while (true) { + var conn = tcp_server.accept() catch continue; + defer conn.stream.close(); - log.info("{s} {s} {s}", .{ @tagName(response.request.method), @tagName(response.request.version), response.request.target }); + var buf: [8192]u8 = undefined; + var server = std.http.Server.init(conn, &buf); + while (server.state == .ready) { + var request = server.receiveHead() catch continue :accept; - response.status = .ok; - response.transfer_encoding = .chunked; - try response.headers.append("connection", "close"); + log.info("{s} {s} {s}", .{ @tagName(request.head.method), @tagName(request.head.version), request.head.target }); - context.onRequest.call(.{ &response }, context.userBindings) catch |err| { - response.status = .internal_server_error; - response.transfer_encoding = .chunked; - try response.headers.append("connection", "close"); - try response.send(); - response.writeAll(@errorName(err)) catch {}; + context.onRequest.call(.{ &request }, context.userBindings) catch |err| { + request.respond(@errorName(err), .{ + .status = .internal_server_error, + .transfer_encoding = .none, + .keep_alive = false, + }) catch {}; }; - try response.finish(); + continue :accept; } } } -pub fn run(allocator: std.mem.Allocator, address: []const u8, port: u16, comptime onRequest: anytype, bindings: anytype) !void { +pub fn run(address: []const u8, port: u16, comptime onRequest: anytype, bindings: anytype) !void { const Context = struct { comptime onRequest: CallableType(onRequest) = Callable(onRequest), userBindings: @TypeOf(bindings), }; - try serve(allocator, address, port, Context { .userBindings = bindings }); + try serve(address, port, Context { .userBindings = bindings }); } diff --git a/flake.lock b/flake.lock index 23f19ef..2727a90 100644 --- a/flake.lock +++ b/flake.lock @@ -93,11 +93,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1711934147, - "narHash": "sha256-hCRDJ+czQfRx9w7zvDC+vUTifvB5nfyE8GSxjTpjeJE=", + "lastModified": 1715217882, + "narHash": "sha256-NZxALOp414o7J7yG+C83fqyYcr66pP0QFsXQRkwXHQI=", "owner": "Cloudef", "repo": "zig2nix", - "rev": "65ce160fae922cd6fad7a2726f533117a6b9f616", + "rev": "a66ba2f42e3e4efcb7e73cf5ca6a4ee436dd2049", "type": "github" }, "original": { diff --git a/src/router.zig b/src/router.zig index 655191d..7ec2ea9 100644 --- a/src/router.zig +++ b/src/router.zig @@ -131,7 +131,7 @@ fn RouteType(comptime method: Method, comptime path: []const u8, comptime handle @compileError(std.fmt.comptimePrint("{s} empty path components are not allowed", .{path})); } - _ = std.Uri.parseWithoutScheme(path) catch @compileError(std.fmt.comptimePrint("invalid route path: {s}", .{path})); + _ = std.Uri.parseAfterScheme("", path) catch @compileError(std.fmt.comptimePrint("invalid route path: {s}", .{path})); const return_type = switch (@typeInfo(handler_info.return_type.?)) { .ErrorUnion => |eu| anyerror!eu.payload,