diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 11 | ||||
-rw-r--r-- | README.md | 17 | ||||
-rw-r--r-- | global.ha | 2 | ||||
-rw-r--r-- | main.ha | 7 | ||||
-rw-r--r-- | req.ha | 44 | ||||
-rw-r--r-- | templates/_footer.htmpl | 2 | ||||
-rw-r--r-- | templates/index.htmpl | 8 | ||||
-rw-r--r-- | url.ha | 6 |
9 files changed, 62 insertions, 38 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 @@ -1,15 +1,2 @@ -# Lindenii Forge - -**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. - -## Architecture - -* Most components are one single daemon written in Hare. -* Because libssh is difficult to use and there aren't many other SSH server - libraries for C or Hare, we will temporarily use - [the gliberlabs SSH library for Go](https://github.com/gliderlabs/ssh) - in a separate process, and communicate via UNIX domain sockets. +**Please check out the `master` branch. Everything that used to be in the +`hare` branch is now in `master`. The `hare` branch is deprectated.** @@ -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; @@ -21,6 +22,8 @@ const usage: [_]getopt::help = [ ('c', "config", "path to configuration file") ]; +let static_fs: nullable *fs::fs = null; + export fn main() void = { const cmd = getopt::parse(os::args, usage...); defer getopt::finish(&cmd); @@ -31,10 +34,12 @@ 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")!; + const server = match (http::listen(ip_addr, port, net::tcp::reuseport, net::tcp::reuseaddr)) { case let this: *http::server => 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/templates/_footer.htmpl b/templates/_footer.htmpl index 71d5318..a0d1987 100644 --- a/templates/_footer.htmpl +++ b/templates/_footer.htmpl @@ -5,5 +5,5 @@ {{ " " }} (<a href="/:/source/">source</a>, {{ " " }} -<a href="https://forge.lindenii.runxiyu.org/lindenii/forge/:/repos/server/">upstream</a>) +<a href="https://forge.lindenii.runxiyu.org/lindenii/forge/:/repos/server/?branch=hare">upstream</a>) {{ end }} 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; }; }; |