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}