diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 11 | ||||
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | global.ha | 2 | ||||
-rw-r--r-- | main.ha | 19 | ||||
-rw-r--r-- | req.ha | 44 | ||||
-rw-r--r-- | static/style.css | 2 | ||||
-rw-r--r-- | templates/index.htmpl | 8 | ||||
-rw-r--r-- | url.ha | 6 |
9 files changed, 75 insertions, 27 deletions
@@ -1,2 +1,3 @@ /forge -/templates.ha +/.templates.ha +/.version.ha @@ -1,5 +1,10 @@ -forge: templates.ha *.ha - hare build -o $@ . +forge: .version.ha .templates.ha *.ha + hare build $(HAREFLAGS) -o $@ . -templates.ha: templates/*.htmpl +.templates.ha: templates/*.htmpl htmplgen -o $@ $^ + +.version.ha: + printf 'def VERSION="%s";\n' $(shell git describe --tags --always --dirty) > $@ + +.PHONY: version.ha @@ -2,9 +2,10 @@ **Work in progress.** -This is the new implementation in the [Hare](https://harelang.org) programming -language. We will set this as the primary branch once it reaches feature parity -with the Go implementation. +I tried to reimplement in the [Hare](https://harelang.org) programming +language. It's one of my favorite programming languages, but sadly due to the +lack of multithreading and since it's still a very unstable moving target, +I'm still mainly working on the Go branch. ## Architecture @@ -5,7 +5,7 @@ let global: struct { ssh_fp: str, } = struct { title: str = "Test Forge", - version: str = "v0.0.0", + version: str = VERSION, ssh_pubkey: str = "pubkey", ssh_fp: str = "fp", }; @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org> // Adapted from template by Willow Barraco <contact@willowbarraco.fr> +use fs; use getopt; use log; use net; @@ -15,13 +16,24 @@ use memio; use io; use fmt; use bufio; +use unix::signal; const usage: [_]getopt::help = [ "Lindenii Forge Server", ('c', "config", "path to configuration file") ]; +let static_fs: nullable *fs::fs = null; + +let running: bool = true; + +export fn sigint_handler(sig: signal::sig, info: *signal::siginfo, ucontext: *opaque) void = { + running = false; +}; + export fn main() void = { + signal::handle(signal::sig::INT, &sigint_handler, signal::flag::NONE, null); + const cmd = getopt::parse(os::args, usage...); defer getopt::finish(&cmd); @@ -31,10 +43,13 @@ export fn main() void = { for (let opt .. cmd.opts) { switch (opt.0) { case 'c' => yield; // TODO: actually handle the config - case => abort(); // unreachable + case => abort("unreachable"); }; }; + static_fs = os::diropen("static")!; + defer fs::close(static_fs as *fs::fs); + const server = match (http::listen(ip_addr, port, net::tcp::reuseport, net::tcp::reuseaddr)) { case let this: *http::server => yield this; @@ -42,7 +57,7 @@ export fn main() void = { }; defer http::server_finish(server); - for (true) { + for (running) { const serv_req = match (http::serve(server)) { case let this: *http::server_request => yield this; @@ -2,15 +2,28 @@ // SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org> use fmt; +use fs; use htmpl; use io; +use mime; use net::http; use net::uri; use strconv; use strings; -fn handlereq(conn: io::handle, request: *http::request) (void | io::error | nomem | uri::invalid) = { - let segments = segments_from_path(request.target.raw_path)?; +fn handlereq(conn: io::handle, request: *http::request) (void | io::error | nomem | fs::error) = { + let segments = match(segments_from_path(request.target.raw_path)) { + case let s: []str => + yield s; + case uri::invalid => + start_response(conn, 400, "text/plain")?; + fmt::fprintln(conn, "Invalid URI")?; + return void; + case nomem => + return nomem; + case => + abort("unreachable"); + }; defer strings::freeall(segments); let trailing_slash: bool = false; @@ -23,7 +36,7 @@ fn handlereq(conn: io::handle, request: *http::request) (void | io::error | nome if (len(segments) == 0) { start_response(conn, 200, "text/html")?; - return tp_index(conn, segments); + return tp_index(conn); }; if (segments[0] == ":") { @@ -40,6 +53,7 @@ fn handlereq(conn: io::handle, request: *http::request) (void | io::error | nome fmt::fprintln(conn, "Error: Blank static endpoint")?; return; }; + let fs_segments = segments[2 ..]; for (let fs_segment .. fs_segments) { if (strings::contains(fs_segment, "/")) { @@ -48,8 +62,28 @@ fn handlereq(conn: io::handle, request: *http::request) (void | io::error | nome return; }; }; - start_response(conn, 501, "text/plain")?; - fmt::fprintln(conn, "Not implemented yet")?; + let fs_segment_path = strings::join("/", fs_segments...)?; + defer free(fs_segment_path); + + let file = match (fs::open(static_fs as *fs::fs, fs_segment_path)) { + case let f: io::handle => yield f; + case fs::error => + start_response(conn, 500, "text/plain")?; + fmt::fprintln(conn, "Filesystem error")?; + return; + }; + defer io::close(file)!; + + let ext = strings::rcut(fs_segments[len(fs_segments) - 1], ".").1; + + let mimetype = match (mime::lookup_ext(ext)) { + case let m: *mime::mimetype => yield m.mime; + case null => yield "application/octet-stream"; + }; + + start_response(conn, 200, mimetype)?; + io::copy(conn, file)?; + case => start_response(conn, 404, "text/plain")?; fmt::fprintln(conn, "Error: Unknown system endpoint")?; diff --git a/static/style.css b/static/style.css index e5398ce..a3acd10 100644 --- a/static/style.css +++ b/static/style.css @@ -6,7 +6,7 @@ /* Base styles and variables */ html { - font-family: sans-serif; + font-family: 'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; /* Yes */ background-color: var(--background-color); color: var(--text-color); --radius-1: 0.32rem; diff --git a/templates/index.htmpl b/templates/index.htmpl index 1b4dd8b..e67cc09 100644 --- a/templates/index.htmpl +++ b/templates/index.htmpl @@ -1,4 +1,4 @@ -{{ define tp_index(handle: io::handle, segments: []str) (void | io::error | nomem) }} +{{ define tp_index(handle: io::handle) (void | io::error | nomem) }} <!DOCTYPE html> <html lang="en"> <head> @@ -7,12 +7,6 @@ </head> <body> {{ render _tp_header(handle, "test", "test") }} -<h2>Path segments</h2> -<ul> - {{ for let s .. segments }} - <li>{{ s }}</li> - {{ end }} -</ul> <div class="padding-wrapper"> <table class="wide rounded"> <thead> @@ -4,7 +4,7 @@ use strings; use net::uri; -// The result must be freed with strings::freeall. +// The result, if not erroring out, must be freed with strings::freeall. fn segments_from_path(s: str) ([]str | nomem | uri::invalid) = { let sp: []str = strings::split(s, "/")?; for (let i = 1z; i < len(sp); i += 1) { @@ -12,9 +12,7 @@ fn segments_from_path(s: str) ([]str | nomem | uri::invalid) = { case let s: str => sp[i - 1] = s; case uri::invalid => - for (let j = 0z; j < i - 1; j += 1) { - free(sp[j]); - }; + strings::freeall(sp[.. i - 1]); return uri::invalid; }; }; |