code wiki / _hdl_build / nx_clean_url.nx
nx_clean_url.nx source
↩ module page · 85 lines · 3942 B
1// nx_clean_url.nx -- S-class CLEAN (extensionless) URL support for the Nishi hosting layer. Operator: "make sure
2// the urls dont need html." Two parts:
3// (1) cu_resolve(request_path) -> doc-root file: the GENERAL rule the sites daemon is missing today (it only
4// hardcodes per-page redirects). "/" -> index.html ; "/foo/" -> foo/index.html ; "/foo" (no ext) -> foo.html
5// (the clean URL) ; "/a.css" (has ext) -> served as-is. Traversal (".." ) REJECTED by construction.
6// (2) cu_audit_links(html) -> count of hrefs that still carry ".html" (so our generated pages can be PROVEN to
7// link cleanly). cu_link_is_clean(href) is the single-link predicate.
8// Sovereign primitive (nx_syscalls only); ready to wire into nx_sites_daemon_v2 (coordinate w/ the hosting
9// workstream -- it's the live production binary). license_tier: ORIGINAL
10import "nx_syscalls.nx"
11
12func cu_app(out: *u8, pos: i64, cap: i64, s: *u8) -> i64 {
13 var p: i64 = pos; var i: i64 = 0
14 while s[i] != (0 as u8) { if p < cap { out[p] = s[i]; p = p + 1 } i = i + 1 }
15 return p
16}
17func cu_match_at(s: *u8, i: i64, n: i64, pat: *u8, plen: i64) -> i64 {
18 if i + plen > n { return 0 }
19 var j: i64 = 0
20 while j < plen { if s[i+j] != pat[j] { return 0 } j = j + 1 }
21 return 1
22}
23// is `needle` a substring of hay[lo..hi)?
24func cu_find_sub(hay: *u8, lo: i64, hi: i64, needle: *u8, nlen: i64) -> i64 {
25 var i: i64 = lo
26 while i + nlen <= hi { if cu_match_at(hay, i, hi, needle, nlen) == 1 { return 1 } i = i + 1 }
27 return 0
28}
29// path contains ".." anywhere -> traversal
30func cu_has_traversal(path: *u8, n: i64) -> i64 {
31 var i: i64 = 0
32 while i + 1 < n { if path[i] == (46 as u8) { if path[i+1] == (46 as u8) { return 1 } } i = i + 1 }
33 return 0
34}
35// does the last path segment (after the final '/') contain a '.' (i.e., it already has a file extension)?
36func cu_seg_has_dot(path: *u8, n: i64) -> i64 {
37 var seg: i64 = 0; var i: i64 = 0
38 while i < n { if path[i] == (47 as u8) { seg = i + 1 } i = i + 1 }
39 var j: i64 = seg
40 while j < n { if path[j] == (46 as u8) { return 1 } j = j + 1 }
41 return 0
42}
43// resolve a REQUEST path (no query string) to a doc-root-relative FILE path. returns length, or -1 if rejected.
44func cu_resolve(path: *u8, n: i64, out: *u8, cap: i64) -> i64 {
45 if cu_has_traversal(path, n) == 1 { return 0 - 1 }
46 var s: i64 = 0
47 if n > 0 { if path[0] == (47 as u8) { s = 1 } }
48 var p: i64 = 0
49 var i: i64 = s
50 while i < n { if p < cap - 1 { out[p] = path[i]; p = p + 1 } i = i + 1 }
51 if p == 0 {
52 p = cu_app(out, 0, cap, "index.html" as *u8) // "/" -> index.html
53 } else {
54 if out[p-1] == (47 as u8) {
55 p = cu_app(out, p, cap, "index.html" as *u8) // "/foo/" -> foo/index.html
56 } else {
57 if cu_seg_has_dot(path, n) == 0 {
58 p = cu_app(out, p, cap, ".html" as *u8) // "/foo" -> foo.html (the clean URL)
59 }
60 // else: already has an extension -> serve as-is
61 }
62 }
63 if p < cap { out[p] = 0 as u8 }
64 return p
65}
66// 1 if a single href is clean (carries no ".html"); 0 otherwise. (Anchors "#x" and "/x" are clean.)
67func cu_link_is_clean(href: *u8) -> i64 {
68 var n: i64 = 0; while href[n] != (0 as u8) { n = n + 1 }
69 if cu_find_sub(href, 0, n, ".html" as *u8, 5) == 1 { return 0 }
70 return 1
71}
72// count hrefs in a built page whose value still carries ".html" (0 == fully clean-linked).
73func cu_audit_links(html: *u8, n: i64) -> i64 {
74 var c: i64 = 0
75 var i: i64 = 0
76 while i + 6 <= n {
77 if cu_match_at(html, i, n, "href=\"" as *u8, 6) == 1 {
78 var j: i64 = i + 6
79 while j < n { if html[j] == (34 as u8) { break } j = j + 1 } // closing quote
80 if cu_find_sub(html, i + 6, j, ".html" as *u8, 5) == 1 { c = c + 1 }
81 i = j + 1
82 } else { i = i + 1 }
83 }
84 return c
85}