nx_lib_http.nx source
↩ module page · 163 lines · 6736 B
1// nx_lib_http.nx -- PURE HTTP routing/response for the sovereign library API.
2// No main, no sockets: request-path -> full HTTP response bytes, straight from
3// nx_lib_store via nx_lib_serve. Gateable in isolation; the daemon nx_lib_httpd
4// wraps it with the socket accept loop.
5// GET /api/works -> JSON array of work_hks
6// GET /api/work/<hk> -> JSON work record (200) or 404
7// license_tier: ORIGINAL
8import "nx_lib_serve.nx"
9import "nx_lib_index.nx"
10import "nx_lib_ingest.nx"
11import "nx_lib_ingest_tsv.nx" // nx_lib_ingest_buf -- the TSV ingest organ deleted by the 07-21 dedupe
12import "nx_lib_page.nx"
13
14func lhd_puts(buf: *u8, off: i64, s: *u8) -> i64 {
15 var o: i64 = off
16 var i: i64 = 0
17 while s[i] != (0 as u8) { buf[o] = s[i]; o = o + 1; i = i + 1 }
18 return o
19}
20
21// append decimal v; nxc2 has no % so mod = x-(x/10)*10.
22func lhd_putdec(buf: *u8, off: i64, v: i64) -> i64 {
23 if v == 0 { buf[off] = 48 as u8; return off + 1 }
24 let tmp: *u8 = sys_mmap(32)
25 var k: i64 = 0
26 var x: i64 = v
27 while x > 0 { tmp[k] = (48 + (x - (x / 10) * 10)) as u8; x = x / 10; k = k + 1 }
28 var o: i64 = off
29 var ri: i64 = k - 1
30 while ri >= 0 { buf[o] = tmp[ri]; o = o + 1; ri = ri - 1 }
31 return o
32}
33
34func lhd_eq(path: *u8, path_n: i64, s: *u8) -> i64 {
35 let sn: i64 = ls_strlen(s)
36 if sn != path_n { return 0 }
37 var i: i64 = 0
38 while i < sn { if path[i] != s[i] { return 0 } i = i + 1 }
39 return 1
40}
41
42func lhd_starts(path: *u8, path_n: i64, pre: *u8) -> i64 {
43 var i: i64 = 0
44 while pre[i] != (0 as u8) {
45 if i >= path_n { return 0 }
46 if path[i] != pre[i] { return 0 }
47 i = i + 1
48 }
49 return 1
50}
51
52// PURE: build the full HTTP response for a request path into out; return length.
53func lhd_response(path: *u8, path_n: i64, out: *u8) -> i64 {
54 // ---- /research edge-prefix alias (ADDITIVE 2026-07-02) ----
55 // The sites edge routes `nishifamily.com /research 8095 stream` via proxy_routes.conf,
56 // and STREAM mode passes the RAW (unstripped) request through -- so accept the full
57 // /research prefix here by delegating to the identical stripped route. Buffered-mode
58 // clients (nx_http_proxy_relay strips before forwarding) are unaffected.
59 if lhd_starts(path, path_n, "/research" as *u8) == 1 {
60 if path_n == 9 { return lhd_page_home(("" as *u8), 0, out) }
61 let anc: i64 = path[9] as i64
62 if anc == 47 { return lhd_response(((path as i64) + 9) as *u8, path_n - 9, out) }
63 if anc == 63 {
64 // "/research?q=x" -> "/?q=x" (re-root the query form)
65 let ab: *u8 = sys_mmap(path_n + 8)
66 ab[0] = 47 as u8
67 var ai: i64 = 9
68 var ak: i64 = 1
69 while ai < path_n { ab[ak] = path[ai]; ak = ak + 1; ai = ai + 1 }
70 return lhd_response(ab, ak, out)
71 }
72 }
73 // ---- sovereign zero-JS HTML frontend (served before the JSON API) ----
74 if lhd_eq(path, path_n, "/" as *u8) == 1 { return lhd_page_home(("" as *u8), 0, out) }
75 if lhd_starts(path, path_n, "/?" as *u8) == 1 {
76 let hqbuf: *u8 = sys_mmap(2048)
77 var hqs: i64 = 0 - 1
78 var hsi: i64 = 0
79 while hsi + 1 < path_n { if hqs < 0 { if path[hsi] == 113 as u8 { if path[hsi + 1] == 61 as u8 { hqs = hsi + 2 } } } hsi = hsi + 1 }
80 var hql: i64 = 0
81 if hqs >= 0 {
82 var hk: i64 = hqs
83 while hk < path_n {
84 let c: i64 = path[hk] as i64
85 if c == 38 { hk = path_n }
86 else { if c == 43 { hqbuf[hql] = 32 as u8; hql = hql + 1; hk = hk + 1 } else { hqbuf[hql] = path[hk]; hql = hql + 1; hk = hk + 1 } }
87 }
88 }
89 return lhd_page_home(hqbuf, hql, out)
90 }
91 if lhd_starts(path, path_n, "/work/" as *u8) == 1 {
92 return lhd_page_work(((path as i64) + 6) as *u8, path_n - 6, out)
93 }
94
95 let body: *u8 = sys_mmap(1048576)
96 var bn: i64 = 0
97 var ok: i64 = 0
98 if lhd_eq(path, path_n, "/api/works" as *u8) == 1 {
99 bn = nx_lib_serve_list(body)
100 ok = 1
101 } else { if lhd_starts(path, path_n, "/api/search" as *u8) == 1 {
102 // GET /api/search?q=<terms> ('+' -> space, '&' ends the value)
103 let qbuf: *u8 = sys_mmap(2048)
104 var qs: i64 = 0 - 1
105 var si: i64 = 0
106 while si + 1 < path_n {
107 if qs < 0 { if path[si] == 113 as u8 { if path[si + 1] == 61 as u8 { qs = si + 2 } } }
108 si = si + 1
109 }
110 var ql: i64 = 0
111 if qs >= 0 {
112 var kk: i64 = qs
113 while kk < path_n {
114 let c: i64 = path[kk] as i64
115 if c == 38 { kk = path_n }
116 else {
117 if c == 43 { qbuf[ql] = 32 as u8; ql = ql + 1; kk = kk + 1 }
118 else { qbuf[ql] = path[kk]; ql = ql + 1; kk = kk + 1 }
119 }
120 }
121 }
122 bn = nx_lib_index_search(qbuf, ql, body) // O(1) persisted inverted index
123 ok = 1
124 } else { if lhd_starts(path, path_n, "/api/work/" as *u8) == 1 {
125 let hkbuf: *u8 = sys_mmap(1024)
126 var i: i64 = 10
127 var k: i64 = 0
128 while i < path_n { hkbuf[k] = path[i]; k = k + 1; i = i + 1 }
129 hkbuf[k] = 0 as u8
130 let jl: i64 = nx_lib_serve_work(hkbuf, body)
131 if jl >= 0 { bn = jl; ok = 1 }
132 } } }
133 if ok == 0 {
134 let r404: *u8 = "HTTP/1.1 404 Not Found\r\nContent-Type: application/json\r\nContent-Length: 14\r\nConnection: close\r\n\r\n{\"error\":\"nf\"}" as *u8
135 return lhd_puts(out, 0, r404)
136 }
137 var o: i64 = 0
138 o = lhd_puts(out, o, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: " as *u8)
139 o = lhd_putdec(out, o, bn)
140 o = lhd_puts(out, o, "\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8)
141 var j: i64 = 0
142 while j < bn { out[o] = body[j]; o = o + 1; j = j + 1 }
143 return o
144}
145
146// PURE: ingest a TSV request body into nx_lib_store, rebuild the search index,
147// return an HTTP 200 {"ingested":N}. Backs POST /api/ingest. Gateable.
148func lhd_ingest_response(body: *u8, body_n: i64, out: *u8) -> i64 {
149 let cnt: i64 = nx_lib_ingest_buf(body, body_n)
150 nx_lib_index_build() // reindex so new works are searchable
151 let jb: *u8 = sys_mmap(256)
152 var jo: i64 = 0
153 jo = lhd_puts(jb, jo, "{\"ingested\":" as *u8)
154 jo = lhd_putdec(jb, jo, cnt)
155 jo = lhd_puts(jb, jo, "}" as *u8)
156 var o: i64 = 0
157 o = lhd_puts(out, o, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: " as *u8)
158 o = lhd_putdec(out, o, jo)
159 o = lhd_puts(out, o, "\r\nConnection: close\r\n\r\n" as *u8)
160 var j2: i64 = 0
161 while j2 < jo { out[o] = jb[j2]; o = o + 1; j2 = j2 + 1 }
162 return o
163}