code wiki / _hdl_build / nx_proxy_route.nx
nx_proxy_route.nx source
↩ module page · 144 lines · 7032 B
1// nx_proxy_route.nx -- SOVEREIGN data-driven PROXY route table (pure Nishi, no TLS, no crypto).
2// The DYNAMIC-routing analog of nx_host_router's static docroot table: a config file maps
3// (host, path-prefix) -> (backend_port, mode) so an app/site route is a CONFIG ROW, not a
4// recompiled `sd2_contains(" /gallery")` branch in the TLS daemon. This closes the nx_host_router
5// gap for DYNAMIC routes -> "migrate a bunch of sites" becomes add-a-row, zero recompile.
6// SOTA basis: reverse-proxy / API-gateway route tables reloadable without rebuild
7// (knowledge/fetched/plat_reverse_proxy.raw, plat_api_mgmt.raw); see
8// knowledge/research/2026-06-29-platform-sclass-exceed-standard.md. license_tier: ORIGINAL
9//
10// Config line: "<host|*> <path-prefix> <backend_port> <mode>" ('#' = comment, blank lines ok)
11// host exact (case-insensitive) or '*' wildcard; exact host wins over '*'
12// path-prefix request-line path prefix, boundary-safe (/gen matches /gen,/gen/,/gen?x NOT /generated)
13// backend_port loopback 127.0.0.1:<port> to reverse-proxy to
14// mode buffered (default,0) | stream (1) | gated (2)
15// Match rule: among all rows whose host+prefix match, LONGEST prefix wins; tie -> exact host beats '*'.
16import "nx_syscalls.nx"
17
18const PR_MODE_BUFFERED: i64 = 0
19const PR_MODE_STREAM: i64 = 1
20const PR_MODE_GATED: i64 = 2
21
22func pr_lower(c: u8) -> u8 {
23 if c >= (65 as u8) { if c <= (90 as u8) { return (c + (32 as u8)) } }
24 return c
25}
26
27// case-insensitive byte-compare of exactly n bytes
28func pr_ieq(a: *u8, b: *u8, n: i64) -> i64 {
29 var i: i64 = 0
30 while i < n { if pr_lower(a[i]) != pr_lower(b[i]) { return 0 } i = i + 1 }
31 return 1
32}
33
34func pr_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
35
36// does literal `lit` equal the token at s of length n?
37func pr_word(s: *u8, n: i64, lit: *u8) -> i64 {
38 if pr_strlen(lit) != n { return 0 }
39 var i: i64 = 0
40 while i < n { if s[i] != lit[i] { return 0 } i = i + 1 }
41 return 1
42}
43
44// parse an unsigned decimal token (len n) -> i64 (non-digits ignored)
45func pr_parse_u(s: *u8, n: i64) -> i64 {
46 var v: i64 = 0
47 var i: i64 = 0
48 while i < n {
49 let c: u8 = s[i]
50 if c >= (48 as u8) { if c <= (57 as u8) { v = v * 10 + ((c as i64) - 48) } }
51 i = i + 1
52 }
53 return v
54}
55
56func pr_mode(s: *u8, n: i64) -> i64 {
57 if pr_word(s, n, "stream" as *u8) == 1 { return PR_MODE_STREAM }
58 if pr_word(s, n, "gated" as *u8) == 1 { return PR_MODE_GATED }
59 return PR_MODE_BUFFERED
60}
61
62// boundary-safe path-prefix test: does `path`(len pn) begin with `pref`(len prefn) at a SEGMENT
63// boundary? prefn>0 required. /gen matches /gen, /gen/, /gen?x; NOT /generated.
64func pr_path_pref(path: *u8, pn: i64, pref: *u8, prefn: i64) -> i64 {
65 if prefn == 0 { return 0 }
66 if prefn > pn { return 0 }
67 var i: i64 = 0
68 while i < prefn { if path[i] != pref[i] { return 0 } i = i + 1 }
69 if pn == prefn { return 1 }
70 let nc: u8 = path[prefn]
71 if nc == (47 as u8) { return 1 } // '/'
72 if nc == (63 as u8) { return 1 } // '?'
73 if pref[prefn - 1] == (47 as u8) { return 1 } // prefix already ended in '/'
74 return 0
75}
76
77// Look up (host, path) in the config buffer. On match: sets *out_port,*out_mode and returns 1.
78// No match -> returns 0 (caller falls through to static serving). Longest-prefix wins; exact host
79// beats '*' at equal prefix length. Reads the buffer ONLY (daemon supplies it hot, per request).
80func pr_lookup(cfg: *u8, cfgn: i64, host: *u8, hn: i64, path: *u8, pn: i64, out_port: *i64, out_mode: *i64) -> i64 {
81 var i: i64 = 0
82 var best_plen: i64 = 0 - 1
83 var best_exact: i64 = 0
84 var best_port: i64 = 0
85 var best_mode: i64 = 0
86 while i < cfgn {
87 // skip leading spaces/tabs
88 while i < cfgn { let c: u8 = cfg[i]; if c == (32 as u8) { i = i + 1 } else { if c == (9 as u8) { i = i + 1 } else { break } } }
89 // comment -> skip to EOL
90 if i < cfgn { if cfg[i] == (35 as u8) { while i < cfgn { if cfg[i] == (10 as u8) { break } i = i + 1 } i = i + 1; continue } }
91 // blank line
92 if i < cfgn { if cfg[i] == (10 as u8) { i = i + 1; continue } }
93 if i < cfgn { if cfg[i] == (13 as u8) { i = i + 1; continue } }
94 if i >= cfgn { break }
95 // token 1: host
96 let h0: i64 = i
97 while i < cfgn { let c: u8 = cfg[i]; if c == (32 as u8) { break } if c == (9 as u8) { break } if c == (10 as u8) { break } if c == (13 as u8) { break } i = i + 1 }
98 let h0len: i64 = i - h0
99 while i < cfgn { let c: u8 = cfg[i]; if c == (32 as u8) { i = i + 1 } else { if c == (9 as u8) { i = i + 1 } else { break } } }
100 // token 2: path-prefix
101 let p0: i64 = i
102 while i < cfgn { let c: u8 = cfg[i]; if c == (32 as u8) { break } if c == (9 as u8) { break } if c == (10 as u8) { break } if c == (13 as u8) { break } i = i + 1 }
103 let p0len: i64 = i - p0
104 while i < cfgn { let c: u8 = cfg[i]; if c == (32 as u8) { i = i + 1 } else { if c == (9 as u8) { i = i + 1 } else { break } } }
105 // token 3: port
106 let n0: i64 = i
107 while i < cfgn { let c: u8 = cfg[i]; if c == (32 as u8) { break } if c == (9 as u8) { break } if c == (10 as u8) { break } if c == (13 as u8) { break } i = i + 1 }
108 let n0len: i64 = i - n0
109 while i < cfgn { let c: u8 = cfg[i]; if c == (32 as u8) { i = i + 1 } else { if c == (9 as u8) { i = i + 1 } else { break } } }
110 // token 4: mode (optional)
111 let m0: i64 = i
112 while i < cfgn { let c: u8 = cfg[i]; if c == (32 as u8) { break } if c == (9 as u8) { break } if c == (10 as u8) { break } if c == (13 as u8) { break } i = i + 1 }
113 let m0len: i64 = i - m0
114 // advance to EOL
115 while i < cfgn { if cfg[i] == (10 as u8) { break } i = i + 1 }
116 i = i + 1
117 // require host + prefix + port present
118 if h0len > 0 { if p0len > 0 { if n0len > 0 {
119 var hostok: i64 = 0
120 var exact: i64 = 0
121 if h0len == 1 { if cfg[h0] == (42 as u8) { hostok = 1 } }
122 if hostok == 0 { if h0len == hn { if pr_ieq(((cfg as i64 + h0) as *u8), host, hn) == 1 { hostok = 1; exact = 1 } } }
123 if hostok == 1 {
124 if pr_path_pref(path, pn, ((cfg as i64 + p0) as *u8), p0len) == 1 {
125 var better: i64 = 0
126 if p0len > best_plen { better = 1 }
127 else { if p0len == best_plen { if exact == 1 { if best_exact == 0 { better = 1 } } } }
128 if better == 1 {
129 best_plen = p0len
130 best_exact = exact
131 best_port = pr_parse_u(((cfg as i64 + n0) as *u8), n0len)
132 best_mode = pr_mode(((cfg as i64 + m0) as *u8), m0len)
133 }
134 }
135 }
136 } } }
137 }
138 if best_plen >= 0 {
139 out_port[0] = best_port
140 out_mode[0] = best_mode
141 return 1
142 }
143 return 0
144}