diff options
Diffstat (limited to 'src/http/handler')
| -rw-r--r-- | src/http/handler/Interface.zig | 44 | ||||
| -rw-r--r-- | src/http/handler/context.zig | 76 | ||||
| -rw-r--r-- | src/http/handler/root.zig | 35 | ||||
| -rw-r--r-- | src/http/handler/static.zig | 13 |
4 files changed, 168 insertions, 0 deletions
diff --git a/src/http/handler/Interface.zig b/src/http/handler/Interface.zig new file mode 100644 index 0000000..2238424 --- /dev/null +++ b/src/http/handler/Interface.zig @@ -0,0 +1,44 @@ +const std = @import("std"); +const Response = @import("../Response.zig"); +const Route = @import("../Route.zig"); + +route: Route, +base: *const anyopaque, +handler: *const fn ( + *const anyopaque, + std.mem.Allocator, + std.mem.Allocator, + std.Io, + *std.http.Server.Request, +) Response, + +pub fn from(instance: anytype, route: Route) @This() { + const PtrT = @TypeOf(instance); + const func = struct { + pub fn handle( + base: *const anyopaque, + gpa: std.mem.Allocator, + arena: std.mem.Allocator, + io: std.Io, + request: *std.http.Server.Request, + ) Response { + return @as(PtrT, @ptrCast(@alignCast(base))).handle(gpa, arena, io, request); + } + }.handle; + + return .{ + .base = @ptrCast(instance), + .handler = func, + .route = route, + }; +} + +pub fn handle( + self: *const @This(), + gpa: std.mem.Allocator, + arena: std.mem.Allocator, + io: std.Io, + request: *std.http.Server.Request, +) Response { + return self.handler(self.base, gpa, arena, io, request); +} diff --git a/src/http/handler/context.zig b/src/http/handler/context.zig new file mode 100644 index 0000000..4e55446 --- /dev/null +++ b/src/http/handler/context.zig @@ -0,0 +1,76 @@ +const std = @import("std"); +const Response = @import("../Response.zig"); +const Route = @import("../Route.zig"); +const Parameter = @import("../Parameter.zig"); +const Interface = @import("Interface.zig"); + +pub fn Context(comptime r: anytype) type { + return struct { + pub const route = Route.fromAny(r); + pub const Params = Parameter.Struct(route); + + pub const Request = struct { + base_request: *std.http.Server.Request, + params: Params, + arena: std.mem.Allocator, + gpa: std.mem.Allocator, + io: std.Io, + + /// Parses the body of the request and returns type `T`. + /// The arena allocator will be used to do the parsing. + pub fn json(self: @This(), T: type) !T { + var buffer: [1024]u8 = undefined; + var reader = std.json.Reader.init(self.arena, self.base_request.readerExpectNone(&buffer)); + return std.json.parseFromTokenSourceLeaky(T, self.arena, &reader, .{}); + } + }; + + const badRequest = struct { + pub fn badRequest(_: Request) Response { + return .with(.bad_request, .string("method does not exist")); + } + }.badRequest; + + get: *const fn (Request) Response = badRequest, + post: *const fn (Request) Response = badRequest, + delete: *const fn (Request) Response = badRequest, + head: *const fn (Request) Response = badRequest, + put: *const fn (Request) Response = badRequest, + connect: *const fn (Request) Response = badRequest, + options: *const fn (Request) Response = badRequest, + trace: *const fn (Request) Response = badRequest, + patch: *const fn (Request) Response = badRequest, + + pub fn handle( + self: *const @This(), + gpa: std.mem.Allocator, + arena: std.mem.Allocator, + io: std.Io, + request: *std.http.Server.Request, + ) Response { + const req: Request = .{ + .base_request = request, + .arena = arena, + .gpa = gpa, + .io = io, + .params = Parameter.parse(Params, &route, request.head.target), + }; + + return (switch (request.head.method) { + .GET => self.get, + .POST => self.post, + .DELETE => self.delete, + .HEAD => self.head, + .PUT => self.put, + .CONNECT => self.connect, + .OPTIONS => self.options, + .TRACE => self.trace, + .PATCH => self.patch, + })(req); + } + + pub fn interface(self: *const @This()) Interface { + return .from(self, route); + } + }; +} diff --git a/src/http/handler/root.zig b/src/http/handler/root.zig new file mode 100644 index 0000000..33fdb84 --- /dev/null +++ b/src/http/handler/root.zig @@ -0,0 +1,35 @@ +pub const Context = @import("context.zig").Context; +pub const Interface = @import("Interface.zig"); +pub const Static = @import("static.zig").Static; + +test "interface" { + const M = struct { + const Response = @import("../Response.zig"); + + const C = Context(.{ "api", "images", .id, "metadata" }); + const context: C = .{ + .post = post, + .get = get, + }; + + const Query = struct { + condition: []const u8, + start: usize, + length: usize, + }; + + fn post(request: C.Request) Response { + const query = request.json(Query) catch return .with(.bad_request, .void); + _ = query; + _ = request.params.id; + + return .ok(.string(request.params.id.as([]const u8))); + } + + fn get(request: C.Request) Response { + return .ok(Response.Message.file(request.io, "./some/path.txt") catch return .with(.not_found, .void)); + } + }; + + _ = M.context.interface(); +} diff --git a/src/http/handler/static.zig b/src/http/handler/static.zig new file mode 100644 index 0000000..d53f213 --- /dev/null +++ b/src/http/handler/static.zig @@ -0,0 +1,13 @@ +const Interface = @import("Interface.zig"); +const Context = @import("context.zig").Context; +const Response = @import("../Response.zig"); + +pub fn Static(route: anytype, content: []const u8) Context(route) { + const get = struct { + fn get(_: Context(route).Request) Response { + return .ok(.string(content)); + } + }.get; + + return .{ .get = get }; +} |