aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.zig.zon2
-rw-r--r--src/main.zig33
-rw-r--r--src/routes/login.zig17
-rw-r--r--src/routes/root.zig57
4 files changed, 94 insertions, 15 deletions
diff --git a/build.zig.zon b/build.zig.zon
index 352c4f1..511d4c1 100644
--- a/build.zig.zon
+++ b/build.zig.zon
@@ -22,7 +22,7 @@
// original project's identity. Thus it is recommended to leave the comment
// on the following line intact, so that it shows up in code reviews that
// modify the field.
- .fingerprint = 0x93de37723f3d5133, // Changing this has security and trust implications.
+ .fingerprint = 0xf901ac632f19fccc, // Changing this has security and trust implications.
// Tracks the earliest Zig version that the package considers to be a
// supported use case.
.minimum_zig_version = "0.15.2",
diff --git a/src/main.zig b/src/main.zig
index 2566120..be43352 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -7,14 +7,36 @@ pub const std_options = std.Options {
const log = std.log.scoped(.main);
+var net_server: std.net.Server = undefined;
+
+fn signal_handler(signo: i32) callconv(.c) void {
+ if (signo == std.os.linux.SIG.INT) {
+ log.info("shutdown", .{});
+ net_server.deinit();
+ std.process.exit(0);
+ }
+}
+
+fn register_sigaction() void {
+ var sa = std.os.linux.Sigaction{
+ .handler = .{ .handler = signal_handler },
+ .mask = std.mem.zeroes(std.os.linux.sigset_t),
+ .flags = 0,
+ };
+
+ _ = std.os.linux.sigaction(std.os.linux.SIG.INT, &sa, null);
+}
+
pub fn main() !void {
+ register_sigaction();
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(gpa.deinit() == .ok);
const allocator = gpa.allocator();
- const address = try std.net.Address.parseIpAndPort("127.0.0.1:8080");
- var net_server = try address.listen(.{});
+ const address = try std.net.Address.parseIpAndPort("0.0.0.0:8080");
+ net_server = try address.listen(.{ .reuse_address = true });
defer net_server.deinit();
log.info("listening on {f}", .{address});
@@ -35,10 +57,7 @@ pub fn main() !void {
var request = http_server.receiveHead() catch continue;
log.info("{} {s}", .{request.head.method, request.head.target});
- const handler = memora.routes.get(request.head.target);
-
- handler(&request, allocator) catch |err| {
- log.err("handler({s}): {}", .{request.head.target, err});
- };
+ const handler_info = memora.routes.get(request.head.target);
+ try handler_info.handle(&request, allocator);
}
}
diff --git a/src/routes/login.zig b/src/routes/login.zig
new file mode 100644
index 0000000..29bf370
--- /dev/null
+++ b/src/routes/login.zig
@@ -0,0 +1,17 @@
+const std = @import("std");
+
+pub fn handler(
+ request: *std.http.Server.Request,
+ allocator: std.mem.Allocator,
+) anyerror!void {
+ var output = std.Io.Writer.Allocating.init(allocator);
+ var stringify = std.json.Stringify { .writer = &output.writer };
+
+ try stringify.write(.{
+ .user = "nathan.reiner",
+ .name = "Nathan Reiner",
+ });
+
+ try output.writer.flush();
+ try request.respond(output.written(), .{});
+}
diff --git a/src/routes/root.zig b/src/routes/root.zig
index 0330404..01952bb 100644
--- a/src/routes/root.zig
+++ b/src/routes/root.zig
@@ -1,15 +1,58 @@
const std = @import("std");
pub const fallback = @import("fallback.zig");
+pub const login = @import("login.zig");
-pub const Handler = *const fn (
- request: *std.http.Server.Request,
- allocator: std.mem.Allocator,
-) anyerror!void;
+pub const HandlerInfo = struct {
+ handler: *const fn (
+ request: *std.http.Server.Request,
+ allocator: std.mem.Allocator,
+ ) anyerror!void,
+ needs_auth: bool,
+ method: std.http.Method,
-pub const handlers = std.StaticStringMap(Handler).initComptime(.{
+ pub fn handle(
+ self: *const @This(),
+ request: *std.http.Server.Request,
+ allocator: std.mem.Allocator,
+ ) !void {
+ if (request.head.method != self.method) {
+ try request.respond("{ \"error\": \"Bad Request\" }", .{ .status = .bad_request });
+ }
+
+ self.handler(request, allocator) 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 => .{ "{ \"error\": \"Internal Server Error\" }", .internal_server_error },
+ };
+
+ try request.respond(response, .{ .status = status_code });
+ };
+ }
+};
+
+pub const handlers = std.StaticStringMap(HandlerInfo).initComptime(.{
+ .{
+ "",
+ HandlerInfo {
+ .handler = fallback.handler,
+ .needs_auth = false,
+ .method = .GET,
+ }
+ },
+ .{
+ "/api/login",
+ HandlerInfo {
+ .handler = login.handler,
+ .needs_auth = false,
+ .method = .POST,
+ }
+ },
});
-pub fn get(path: []const u8) Handler {
- return handlers.get(path) orelse fallback.handler;
+pub fn get(path: []const u8) HandlerInfo {
+ return (handlers.getLongestPrefix(std.mem.trimEnd(u8, path, "/")) orelse unreachable).value;
}