aboutsummaryrefslogtreecommitdiff
path: root/src/routes/handler-info.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes/handler-info.zig')
-rw-r--r--src/routes/handler-info.zig225
1 files changed, 0 insertions, 225 deletions
diff --git a/src/routes/handler-info.zig b/src/routes/handler-info.zig
deleted file mode 100644
index ca4babd..0000000
--- a/src/routes/handler-info.zig
+++ /dev/null
@@ -1,225 +0,0 @@
-const std = @import("std");
-
-const memora = @import("memora");
-const Context = memora.Context;
-const Storage = memora.Storage;
-
-const config = @import("config");
-
-const Access = @import("access.zig").Access;
-
-const log = std.log.scoped(.handler_info);
-
-const Self = @This();
-
-const Handler = *const fn (*Context) anyerror!memora.Stream;
-
-get: ?Handler,
-head: ?Handler,
-post: ?Handler,
-put: ?Handler,
-delete: ?Handler,
-connect: ?Handler,
-options: ?Handler,
-trace: ?Handler,
-patch: ?Handler,
-access: Access,
-
-inline fn handler_from_method(self: *const Self, method: std.http.Method) ?Handler {
- return switch (method) {
- .GET => self.get,
- .HEAD => self.head,
- .POST => self.post,
- .PUT => self.put,
- .DELETE => self.delete,
- .CONNECT => self.connect,
- .OPTIONS => self.options,
- .TRACE => self.trace,
- .PATCH => self.patch,
- };
-}
-
-inline fn get_fingerprint(cookie: []const u8) []const u8 {
- const start = std.mem.indexOf(u8, cookie, "fingerprint=") orelse return "";
- const end = std.mem.indexOf(u8, cookie[start + 12..], " ") orelse return cookie[start + 12..];
- return cookie[start..end + 1];
-}
-
-pub fn handle(
- self: *const Self,
- request: *std.http.Server.Request,
- storage: *Storage,
- allocator: std.mem.Allocator,
-) !void {
- const target = try allocator.dupe(u8, request.head.target);
- defer allocator.free(target);
-
- const handler = self.handler_from_method(request.head.method) orelse return request.respond(
- "{ \"error\": \"Bad Request\" }",
- .{ .status = .bad_request }
- );
-
- const cookie = cookie: {
- var iterator = request.iterateHeaders();
- while (iterator.next()) |header| {
- if (std.ascii.eqlIgnoreCase(header.name, "cookie")) {
- break :cookie header.value;
- }
- }
- break :cookie "";
- };
-
- var arena = std.heap.ArenaAllocator.init(allocator);
- defer arena.deinit();
- var context: Context = .{
- .request = request,
- .storage = storage,
- .allocator = arena.allocator(),
- .fingerprint = get_fingerprint(cookie),
- };
-
- if (!comptime config.disable_auth) {
- const allowed = switch (self.access) {
- .everyone => true,
- .users => storage.sessions.get(storage, context.fingerprint) != null,
- .admins => admin: {
- if (storage.sessions.get(storage, context.fingerprint)) |session| {
- break :admin session.info.is_admin;
- }
- break :admin false;
- },
- };
-
- if (!allowed) {
- return request.respond(
- "{ \"error\": \"Forbidden\" }",
- .{ .status = .forbidden }
- );
- }
- }
-
- var stream = handler(&context) catch |err| {
- const response, const status_code: std.http.Status = switch (err) {
- error.BadRequest => .{ "{ \"error\": \"Bad Request\" }", .bad_request },
- error.Unauthorized => .{ "{ \"error\": \"Unauthorized\" }", .unauthorized },
- error.Forbidden => .{ "{ \"error\": \"Forbidden\" }", .forbidden },
- error.NotFound => .{ "{ \"error\": \"Not Found\" }", .not_found },
- else => blk: {
- log.err("handler for '{s}' returned {}", .{target, err});
- break :blk .{ "{ \"error\": \"Internal Server Error\" }", .internal_server_error };
- },
- };
-
- return request.respond(response, .{ .status = status_code });
- };
- defer stream.close();
-
- var headers: std.ArrayList(std.http.Header) = .empty;
- defer headers.deinit(allocator);
-
- try headers.append(allocator, .{
- .name = "Content-Type",
- .value = context.response.headers.content_type
- });
-
- try headers.append(allocator, .{
- .name = "Service-Worker-Allowed",
- .value = "/",
- });
-
- if (context.response.headers.fingerprint) |auth_token| {
- var value = std.Io.Writer.Allocating.init(arena.allocator());
-
- try value.writer.print("fingerprint={s}; Secure; Path=/; Max-Age={}", .{
- auth_token,
- storage.config.session_expires_after / std.time.ns_per_s,
- });
-
- try headers.append(allocator, .{
- .name = "Set-Cookie",
- .value = value.written(),
- });
- }
-
- var read_buffer: [1024]u8 = undefined;
- var reader = stream.reader(&read_buffer);
-
- var write_buffer: [1024]u8 = undefined;
- var body_writer = try request.respondStreaming(&write_buffer, .{
- .respond_options = .{
- .extra_headers = headers.items,
- .transfer_encoding = .chunked,
- },
- });
-
- _ = try reader.streamRemaining(&body_writer.writer);
- try body_writer.end();
-}
-
-fn HandlerWrapper(T: type, name: []const u8) type {
- const info = @typeInfo(@TypeOf(@field(T, name)));
- const return_type = info.@"fn".return_type orelse void;
- const payload_type = @typeInfo(return_type).error_union.payload;
-
- return struct {
- pub fn call(ctx: *Context) anyerror!memora.Stream {
- const args = args: {
- const tuple = std.meta.fields(std.meta.ArgsTuple(@TypeOf(@field(T, name))));
-
- if (tuple.len == 1) {
- break :args .{ ctx };
- } else if (tuple.len == 2) {
- const Body = tuple[1].@"type";
-
- var writer = std.Io.Writer.Allocating.init(ctx.allocator);
- const interface = &writer.writer;
-
- var buffer: [1024]u8 = undefined;
- const reader = try ctx.request.readerExpectContinue(&buffer);
-
- try reader.streamExact64(interface, ctx.request.head.content_length orelse 0);
-
- const body = std.json.parseFromSliceLeaky(
- Body,
- ctx.allocator,
- writer.written(),
- .{},
- ) catch |err| {
- log.warn("failed to parse JSON {}", .{err});
- return error.BadRequest;
- };
- break :args .{ ctx, body };
- } else {
- @compileError("invalid amount of arguments for request function");
- }
- };
-
- if (payload_type == memora.Stream) {
- return @call(.auto, @field(T, name), args);
- } else if (payload_type == void) {
- try @call(.auto, @field(T, name), args);
- return memora.Stream.from_buffer("{}");
- } else {
- var writer = std.Io.Writer.Allocating.init(ctx.allocator);
- var stringify = std.json.Stringify { .writer = &writer.writer };
- try stringify.write(try @call(.auto, @field(T, name), args));
- return memora.Stream.from_buffer(writer.written());
- }
- }
- };
-}
-
-pub fn from_type(T: type) @This() {
- return Self {
- .get = if (@hasDecl(T, "get")) HandlerWrapper(T, "get").call else null,
- .head = if (@hasDecl(T, "head")) HandlerWrapper(T, "head").call else null,
- .post = if (@hasDecl(T, "post")) HandlerWrapper(T, "post").call else null,
- .put = if (@hasDecl(T, "put")) HandlerWrapper(T, "put").call else null,
- .delete = if (@hasDecl(T, "delete")) HandlerWrapper(T, "delete").call else null,
- .connect = if (@hasDecl(T, "connect")) HandlerWrapper(T, "connect").call else null,
- .options = if (@hasDecl(T, "options")) HandlerWrapper(T, "options").call else null,
- .trace = if (@hasDecl(T, "trace")) HandlerWrapper(T, "trace").call else null,
- .patch = if (@hasDecl(T, "patch")) HandlerWrapper(T, "patch").call else null,
- .access = T.access,
- };
-}