code wiki / (root) / nx_fin_serve.nx

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}