aboutsummaryrefslogtreecommitdiff
path: root/src/http/handler.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/http/handler.zig')
-rw-r--r--src/http/handler.zig149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/http/handler.zig b/src/http/handler.zig
new file mode 100644
index 0000000..2220fd0
--- /dev/null
+++ b/src/http/handler.zig
@@ -0,0 +1,149 @@
+const std = @import("std");
+
+const Response = @import("Response.zig");
+const Route = @import("Route.zig");
+const Parameter = @import("Parameter.zig");
+
+pub fn Context(comptime r: anytype) type {
+ return struct {
+ const route = Route.fromAny(r);
+ const Params = Parameter.Struct(route);
+
+ 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);
+ }
+ };
+}
+
+pub const Interface = struct {
+ 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);
+ }
+};
+
+test "interface" {
+ const M = struct {
+ 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();
+}