Something went wrong. Try again.
🦦 a tiny cute lua web framework
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191const std = @import("std");const Io = std.Io;const Allocator = std.mem.Allocator;const http = std.http;const Request = http.Server.Request;const net = Io.net;const Server = net.Server;
const fern = @import("fern");
pub const Config = @import("config.zig");pub const data = @import("data.zig");pub const gen = @import("gen.zig");pub const handler = @import("handler.zig");const Connection = handler.Connection;pub const helpers = @import("helpers.zig");pub const lua = @import("lua/root.zig");pub const Router = @import("router.zig");
pub var build: bool = false;pub var dev: bool = false;pub var rev: ?[]const u8 = null; // null indicates a dev build
pub var config: *const Config = undefined;
var socket_io: Io = undefined;var addr: net.IpAddress = undefined;pub var nserver: ?Server = null;var closed = false;
pub var router: Router = undefined;
// allocator ==================================================================
pub var allocator: Allocator = undefined;
pub fn init(io: Io, init_allocator: Allocator, cfg: *const Config) !void { allocator = init_allocator;
config = cfg;
fern.debug().ctx("maivi.init") .str("root", config.root) .str("base_url", config.base_url) .boolean("dev", dev) .log();
router = .init();
data.init(); errdefer data.deinit();
try lua.init(); errdefer lua.deinit();
try data.load(io);}
pub fn deinit(io: Io) void { fern.trace().ctx("maivi.deinit").log();
router.deinit();
data.deinit(); lua.deinit();
if (nserver == null) return; nserver.?.deinit(io);}
// io handle will be used for all net handlingpub fn server(io: Io) !void { socket_io = io; addr = try .resolve(socket_io, "127.0.0.1", config.port); nserver = try addr.listen(socket_io, .{ .reuse_address = true });}
fn shutdown(signo: std.os.linux.SIG) callconv(.c) void { fern.info().ctx("main") .fmt("sig", "{}", .{signo}).log();
if (nserver == null) return; closed = true; socket_io.vtable.netShutdown(socket_io.userdata, nserver.?.socket.handle, .recv) catch |e| fern.err().err(e).log();}
pub fn listen(io: Io) !void { if (nserver == null) return error.ServerNotInitialized;
fern.info().ctx("maivi.listen") .msg("listening") .int("port", config.port) .int("pid", std.c.getpid()) .log();
std.posix.sigaction(std.posix.SIG.INT, &.{ .handler = .{ .handler = shutdown }, .mask = std.posix.sigemptyset(), .flags = 0, }, null); std.posix.sigaction(std.posix.SIG.TERM, &.{ .handler = .{ .handler = shutdown }, .mask = std.posix.sigemptyset(), .flags = 0, }, null);
var queue: Io.Queue(net.Stream) = .init(&.{}); var task = Io.async(io, consume, .{ io, &queue }); defer task.cancel(io) catch |e| { fern.err().err(e).log(); };
while (!closed) { const stream = nserver.?.accept(io) catch |e| { fern.err().err(e).log(); continue; }; queue.putOne(io, stream) catch |e| { fern.err().err(e).log(); continue; }; }}
pub fn consume(io: Io, queue: *Io.Queue(net.Stream)) !void { while (!closed) { const stream = try queue.getOne(io); handler.task(io, stream) catch |e| { fern.err().err(e).log(); continue; }; }}
// handlers ===================================================================
// try:// with(router) $uri// else:// with(build) $uri $uri/index.html// with(static) $uripub fn handle(conn: *Connection) !void { const request = conn.request;
try lua.trigger(conn.lua, Connection, conn, .handle, .{ .path = conn.path, .pattern = conn.path, }); if (conn.fallen) return;
const path = router.paths.get(conn.path) orelse return handle_fs(conn);
if (std.mem.findScalar( std.http.Method, path.config.allowed_methods, request.head.method, ) == null) return request.respond("", .{ .status = .method_not_allowed, });
try path.action(conn);
// TODO: pre-register all static & build paths // if (dev) // try handle_fs(request);}
fn handle_fs(conn: *Connection) !void { const build_dir = try Io.Dir.openDirAbsolute(conn.io, config.buildroot, .{}); defer build_dir.close(conn.io);
if (std.mem.eql(u8, conn.path, "/")) { if (try helpers.try_path(conn, build_dir, "/index.html")) return; return error.FileNotFound; }
if (try helpers.try_path(conn, build_dir, null)) return;
const path = try std.mem.join(allocator, "/", &.{ conn.path, "index.html" }); defer allocator.free(path); if (try helpers.try_path(conn, build_dir, path)) return;
const static_dir = try Io.Dir.openDirAbsolute(conn.io, config.staticroot, .{}); defer static_dir.close(conn.io);
if (try helpers.try_path(conn, static_dir, null)) return;
return error.FileNotFound;}