nx_fin_serve.nx source
↩ module page · 108 lines · 5989 B
1// nx_fin_serve.nx -- standalone loopback HTTP server for the finance dashboard. Composes nx_http_server
2// (bind/listen/accept, loopback) and serves fin_stack.html as text/html. ADDITIVE + ISOLATED: a NEW service on
3// a caller-chosen port that touches NOTHING existing -> never-brick by construction (mirrors nx_tools_api_serve).
4// Reads the HTML FRESH per request (dual path: fin_stack.html in cwd, else web_assets/fin_stack.html) so a
5// version bump = regenerate + push the HTML, no server redeploy. sites.elf proxies nishifamily.com/finance ->
6// 127.0.0.1:<port> (mode gated = login gate).
7// nx_fin_serve serve [port] -- accept-loop, serve the dashboard
8// nx_fin_serve probe <port> <path> -- sovereign plain-HTTP self-test of the live round-trip
9// license_tier: ORIGINAL
10import "nx_http_server.nx"
11import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
12const FS_MAGIC_8192: i64 = 8192
13const FS_MAGIC_4096: i64 = 4096
14const FS_MAGIC_2048: i64 = 2048
15
16const FS_PORT_DEFAULT: i64 = 18097
17const FS_CAP: i64 = 524288
18
19func fs_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
20func fs_puts(s: *u8) -> i64 { sys_write(1, s, fs_slen(s)); return 0 }
21func fs_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64 = off; var i: i64 = 0; while s[i] != (0 as u8) { dst[o] = s[i]; o = o + 1; i = i + 1 } return o }
22func fs_catn(dst: *u8, off: i64, v: i64) -> i64 {
23 if v == 0 { dst[off] = 48 as u8; return off + 1 }
24 let t: *u8 = sys_mmap(24); var m: i64 = v; var k: i64 = 0
25 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
26 var o: i64 = off; var i: i64 = k - 1; while i >= 0 { dst[o] = t[i]; o = o + 1; i = i - 1 } return o
27}
28func fs_atoi(s: *u8) -> i64 { var v: i64 = 0; var i: i64 = 0; while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c < 48 { return v } if c > 57 { return v } v = v*10 + (c - 48); i = i + 1 } return v }
29
30// read the dashboard HTML into html[0..]; returns byte count (0 if not found). Tries cwd then web_assets/.
31func fs_read_html(html: *u8, cap: i64) -> i64 {
32 var fd: i64 = sys_openat_rd("fin_stack.html" as *u8)
33 if fd < 0 { fd = sys_openat_rd("web_assets/fin_stack.html" as *u8) }
34 if fd < 0 { return 0 }
35 var o: i64 = 0
36 var r: i64 = sys_read(fd, ((html as i64) + o) as *u8, cap - o)
37 while r > 0 { o = o + r; r = sys_read(fd, ((html as i64) + o) as *u8, cap - o) }
38 sys_close(fd)
39 return o
40}
41
42// build the full HTTP response (headers + body) into out from html[0..hn); return length.
43func fs_response(out: *u8, html: *u8, hn: i64) -> i64 {
44 if hn <= 0 { return fs_cat(out, 0, "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\ndashboard not found\n" as *u8) }
45 var o: i64 = fs_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " as *u8)
46 o = fs_catn(out, o, hn)
47 o = fs_cat(out, o, "\r\nConnection: close\r\n\r\n" as *u8)
48 var i: i64 = 0
49 while i < hn { out[o] = html[i]; o = o + 1; i = i + 1 }
50 return o
51}
52
53func fs_serve(port: i64) -> i64 {
54 let addr: *u8 = sys_mmap(16)
55 nx_http_server_addr_loopback(addr, port)
56 let v: *i64 = sys_mmap(16) as *i64
57 let lfd: i64 = nx_http_server_listen(addr, 16, v)
58 if lfd < 0 { fs_puts("nx_fin_serve LISTEN-FAIL\n" as *u8); sys_exit(1); return 1 }
59 let bnr: *u8 = sys_mmap(64); var bo: i64 = fs_cat(bnr, 0, "nx_fin_serve LIVE on 127.0.0.1:" as *u8); bo = fs_catn(bnr, bo, port); bnr[bo] = 10 as u8; sys_write(1, bnr, bo + 1)
60 let req: *u8 = sys_mmap(FS_MAGIC_8192)
61 let html: *u8 = sys_mmap(FS_CAP)
62 let out: *u8 = sys_mmap(FS_CAP + FS_MAGIC_4096)
63 var run: i64 = 1
64 while run == 1 {
65 let cfd: i64 = nx_http_server_accept_one(lfd, v)
66 if cfd >= 0 {
67 let n: i64 = sys_read(cfd, req, FS_MAGIC_8192)
68 if n > 0 {
69 let hn: i64 = fs_read_html(html, FS_CAP)
70 let rn: i64 = fs_response(out, html, hn)
71 nx_http_server_send_response(cfd, out, rn)
72 } else { sys_close(cfd) }
73 }
74 }
75 return 0
76}
77
78func fs_probe(port: i64, path: *u8) -> i64 {
79 let addr: *u8 = sys_mmap(16)
80 nx_http_server_addr_loopback(addr, port)
81 let fd: i64 = sys_socket(2, 1, 0)
82 if fd < 0 { fs_puts("PROBE socket-fail\n" as *u8); sys_exit(1); return 1 }
83 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { fs_puts("PROBE connect-fail\n" as *u8); sys_close(fd); sys_exit(1); return 1 }
84 let req: *u8 = sys_mmap(FS_MAGIC_2048)
85 var o: i64 = fs_cat(req, 0, "GET " as *u8); o = fs_cat(req, o, path); o = fs_cat(req, o, " HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n" as *u8)
86 sys_write(fd, req, o)
87 let buf: *u8 = sys_mmap(FS_CAP + FS_MAGIC_4096); var total: i64 = 0; var run: i64 = 1
88 while run == 1 { let r: i64 = sys_read(fd, ((buf as i64) + total) as *u8, FS_CAP + FS_MAGIC_4096 - total); if r <= 0 { run = 0 } else { total = total + r } }
89 sys_close(fd)
90 var show: i64 = total; if show > 320 { show = 320 }
91 sys_write(1, buf, show); fs_puts("\n... [" as *u8); let mb: *u8 = sys_mmap(32); let mo: i64 = fs_catn(mb, 0, total); sys_write(1, mb, mo); fs_puts(" bytes total]\n" as *u8)
92 return 0
93}
94
95func main(argc: i64, argv: *i64) -> i64 {
96 if argc < 2 { fs_puts("usage: nx_fin_serve serve [port] | probe <port> <path>\n" as *u8); sys_exit(2); return 2 }
97 let mode: *u8 = argv[1] as *u8
98 if mode[0] == (115 as u8) { // 's' = serve
99 var port: i64 = FS_PORT_DEFAULT
100 if argc >= 3 { port = fs_atoi(argv[2] as *u8) }
101 return fs_serve(port)
102 }
103 if mode[0] == (112 as u8) { // 'p' = probe
104 if argc < 4 { fs_puts("usage: nx_fin_serve probe <port> <path>\n" as *u8); sys_exit(2); return 2 }
105 return fs_probe(fs_atoi(argv[2] as *u8), argv[3] as *u8)
106 }
107 fs_puts("unknown mode (serve|probe)\n" as *u8); sys_exit(2); return 2
108}