code wiki / _hdl_build / nx_host_router.nx
nx_host_router.nx source
↩ module page · 931 lines · 49520 B
1// nx_host_router.nx -- S-class sovereign multi-site host router (pure Nishi, no TLS, no crypto).
2// Config-driven vhost table + PER-REQUEST file serving = HOT CONTENT LOADING: push a file, it is
3// live on the next request, NO recompile. Path-traversal-safe. This is L5 (content) of the sovereign
4// hosting design (knowledge/research/2026-06-05-sovereign-web-hosting.md). Composed into the TLS
5// daemon, which supplies L3/L4. license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_fio.nx"
8import "nx_range_header.nx"
9const HR_S2_FILE_STREAM: i64 = 6
10// SOVEREIGN DEFLATE (2026-08-08). The estate's own RFC-1951 compressor has existed and been
11// gunzip-byte-identity proven since 2026-07-20, while this router emitted no Content-Encoding at
12// all -- nx_gzip.nx's header literally calls compression "the #1 remaining HOSTING SOTA gap".
13// The ONLY thing keeping them apart was that nx_gzip.nx defines main(), and NishiLang cannot
14// import a module with main(). nx_gzip_lib.nx is that file with main() renamed, so the deflate
15// code here is byte-identical to the KAT-proven original and inherits its correctness oracle.
16import "nx_gzip_lib.nx"
17const HR_MAGIC_9216: i64 = 9216
18const HR_MAGIC_8192: i64 = 8192
19const HR_MAGIC_1024: i64 = 1024
20const HR_MAGIC_4096: i64 = 4096
21const HR_MAGIC_5120: i64 = 5120
22const HR_MAGIC_2048: i64 = 2048
23const HR_MAGIC_4095: i64 = 4095
24
25func hr_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
26
27func hr_copy(dst: *u8, src: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { dst[i] = src[i]; i = i + 1 } return n }
28
29func hr_eq(a: *u8, b: *u8, n: i64) -> i64 {
30 var i: i64 = 0
31 while i < n { if a[i] != b[i] { return 0 } i = i + 1 }
32 return 1
33}
34
35func hr_lower(c: u8) -> u8 {
36 if c >= (65 as u8) { if c <= (90 as u8) { return (c + (32 as u8)) } }
37 return c
38}
39
40func hr_ieq(a: *u8, b: *u8, n: i64) -> i64 {
41 var i: i64 = 0
42 while i < n { if hr_lower(a[i]) != hr_lower(b[i]) { return 0 } i = i + 1 }
43 return 1
44}
45
46// Decimal-render v into out; return digit count (no modulo operator dependency).
47func hr_putdec(out: *u8, v: i64) -> i64 {
48 if v == 0 { out[0] = (48 as u8); return 1 }
49 let tmp: *u8 = sys_mmap(32)
50 var n: i64 = 0
51 var x: i64 = v
52 while x > 0 {
53 let q: i64 = x / 10
54 let r: i64 = x - q * 10
55 tmp[n] = ((48 + r) as u8)
56 n = n + 1
57 x = q
58 }
59 var i: i64 = 0
60 while i < n { out[i] = tmp[n - 1 - i]; i = i + 1 }
61 return n
62}
63
64// Extract the request path ("GET <path> HTTP/1.1") into out (NUL-terminated); drop any ?query.
65func hr_req_path(req: *u8, reqn: i64, out: *u8, cap: i64) -> i64 {
66 var i: i64 = 0
67 while i < reqn { if req[i] == (32 as u8) { break } i = i + 1 }
68 i = i + 1
69 var j: i64 = 0
70 while i < reqn {
71 let c: u8 = req[i]
72 if c == (32 as u8) { break }
73 if c == (63 as u8) { break }
74 if j < cap - 1 { out[j] = c; j = j + 1 }
75 i = i + 1
76 }
77 out[j] = (0 as u8)
78 return j
79}
80
81// Extract the ?query of the request line INCLUDING the leading '?' (empty -> 0). seq365: the canonical
82// 301 used to rebuild Location from the PATH alone, silently dropping ?room=&k=... from parameterized
83// links (family invites, probe URLs). Redirects must CARRY the query; this is its one extractor.
84func hr_req_query(req: *u8, reqn: i64, out: *u8, cap: i64) -> i64 {
85 var i: i64 = 0
86 while i < reqn { if req[i] == (32 as u8) { break } i = i + 1 }
87 i = i + 1
88 var q: i64 = 0 - 1
89 while i < reqn {
90 let c: u8 = req[i]
91 if c == (32 as u8) { break }
92 if c == (63 as u8) { q = i; break }
93 i = i + 1
94 }
95 if q < 0 { out[0] = (0 as u8); return 0 }
96 var j: i64 = 0
97 while q < reqn {
98 let c2: u8 = req[q]
99 if c2 == (32 as u8) { break }
100 if c2 == (13 as u8) { break }
101 if c2 == (10 as u8) { break }
102 if j < cap - 1 { out[j] = c2; j = j + 1 }
103 q = q + 1
104 }
105 out[j] = (0 as u8)
106 return j
107}
108
109// Extract the Host header (lowercased, port stripped) into out (NUL-terminated).
110func hr_req_host(req: *u8, reqn: i64, out: *u8, cap: i64) -> i64 {
111 var i: i64 = 0
112 var found: i64 = 0 - 1
113 while i + 5 <= reqn {
114 if hr_ieq(((req as i64 + i) as *u8), "host:" as *u8, 5) == 1 { found = i + 5; break }
115 i = i + 1
116 }
117 if found < 0 { out[0] = (0 as u8); return 0 }
118 var k: i64 = found
119 while k < reqn { if req[k] == (32 as u8) { k = k + 1 } else { break } }
120 var j: i64 = 0
121 while k < reqn {
122 let c: u8 = req[k]
123 if c == (13 as u8) { break }
124 if c == (10 as u8) { break }
125 if c == (58 as u8) { break }
126 if c == (32 as u8) { break }
127 if j < cap - 1 { out[j] = hr_lower(c); j = j + 1 }
128 k = k + 1
129 }
130 out[j] = (0 as u8)
131 return j
132}
133
134// Find the doc root for host in the config (lines "<host> <root>"; '#' comment; '*' wildcard default).
135// Exact host wins over wildcard regardless of line order. Returns root length (0 = no match).
136func hr_lookup_root(cfg: *u8, cfgn: i64, host: *u8, hostn: i64, out: *u8, cap: i64) -> i64 {
137 var i: i64 = 0
138 var wild: i64 = 0 - 1
139 var wildlen: i64 = 0
140 while i < cfgn {
141 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 } } }
142 if i < cfgn {
143 if cfg[i] == (35 as u8) {
144 while i < cfgn { if cfg[i] == (10 as u8) { break } i = i + 1 }
145 i = i + 1
146 continue
147 }
148 }
149 let htok: i64 = i
150 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 }
151 let htoklen: i64 = i - htok
152 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 } } }
153 let rtok: i64 = i
154 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 }
155 let rtoklen: i64 = i - rtok
156 while i < cfgn { if cfg[i] == (10 as u8) { break } i = i + 1 }
157 i = i + 1
158 if htoklen == hostn {
159 if hr_eq(((cfg as i64 + htok) as *u8), host, hostn) == 1 {
160 if rtoklen > 0 { hr_copy(out, ((cfg as i64 + rtok) as *u8), rtoklen); out[rtoklen] = (0 as u8); return rtoklen }
161 }
162 }
163 if htoklen == 1 { if cfg[htok] == (42 as u8) { if rtoklen > 0 { wild = rtok; wildlen = rtoklen } } }
164 }
165 if wild >= 0 { if wildlen > 0 { hr_copy(out, ((cfg as i64 + wild) as *u8), wildlen); out[wildlen] = (0 as u8); return wildlen } }
166 return 0
167}
168
169// Reject path traversal / NUL / non-absolute. Conservative: any ".." anywhere fails.
170func hr_path_safe(path: *u8, pathn: i64) -> i64 {
171 if pathn == 0 { return 0 }
172 if path[0] != (47 as u8) { return 0 }
173 var i: i64 = 0
174 while i < pathn {
175 if path[i] == (0 as u8) { return 0 }
176 if path[i] == (46 as u8) { if i + 1 < pathn { if path[i + 1] == (46 as u8) { return 0 } } }
177 i = i + 1
178 }
179 return 1
180}
181
182// Join root + path into out; append index.html when the path ends in '/'.
183func hr_resolve(root: *u8, rootn: i64, path: *u8, pathn: i64, out: *u8, cap: i64) -> i64 {
184 var j: i64 = 0
185 var i: i64 = 0
186 while i < rootn { if j < cap - 1 { out[j] = root[i]; j = j + 1 } i = i + 1 }
187 if j > 0 { if out[j - 1] == (47 as u8) { j = j - 1 } }
188 i = 0
189 while i < pathn { if j < cap - 1 { out[j] = path[i]; j = j + 1 } i = i + 1 }
190 if pathn > 0 {
191 if path[pathn - 1] == (47 as u8) {
192 // path ends in '/': append index.html
193 let idx: *u8 = "index.html" as *u8
194 var k: i64 = 0
195 while idx[k] != (0 as u8) { if j < cap - 1 { out[j] = idx[k]; j = j + 1 } k = k + 1 }
196 } else {
197 // bare path: if the last segment has no '.', it is a directory ->
198 // append /index.html so a CLEAN URL (e.g. /econsim) serves the page
199 // instead of reading the dir as a 0-byte octet-stream download.
200 var seg_has_dot: i64 = 0
201 var s: i64 = pathn - 1
202 while s >= 0 {
203 if path[s] == (47 as u8) { break }
204 if path[s] == (46 as u8) { seg_has_dot = 1; break }
205 s = s - 1
206 }
207 if seg_has_dot == 0 {
208 let idx2: *u8 = "/index.html" as *u8
209 var k2: i64 = 0
210 while idx2[k2] != (0 as u8) { if j < cap - 1 { out[j] = idx2[k2]; j = j + 1 } k2 = k2 + 1 }
211 }
212 }
213 }
214 out[j] = (0 as u8)
215 return j
216}
217
218// MIME by extension.
219func hr_ctype(file: *u8, filen: i64) -> *u8 {
220 var dot: i64 = 0 - 1
221 var i: i64 = 0
222 while i < filen { if file[i] == (46 as u8) { dot = i } i = i + 1 }
223 if dot < 0 { return "application/octet-stream" as *u8 }
224 let ext: *u8 = (file as i64 + dot) as *u8
225 if hr_ieq(ext, ".html" as *u8, 5) == 1 { return "text/html; charset=utf-8" as *u8 }
226 if hr_ieq(ext, ".htm" as *u8, 4) == 1 { return "text/html; charset=utf-8" as *u8 }
227 if hr_ieq(ext, ".css" as *u8, 4) == 1 { return "text/css; charset=utf-8" as *u8 }
228 if hr_ieq(ext, ".json" as *u8, 5) == 1 { return "application/json" as *u8 }
229 // .xml (2026-07-30): sitemap.xml was being served as application/octet-stream because the map had no
230 // XML row -- the one file on the site whose entire purpose is to be parsed by a machine was the one
231 // told it was an opaque blob. RFC 7303 media type; sitemaps.org expects an XML content type.
232 if hr_ieq(ext, ".xml" as *u8, 4) == 1 { return "application/xml; charset=utf-8" as *u8 }
233 if hr_ieq(ext, ".js" as *u8, 3) == 1 { return "application/javascript" as *u8 }
234 if hr_ieq(ext, ".png" as *u8, 4) == 1 { return "image/png" as *u8 }
235 if hr_ieq(ext, ".jpg" as *u8, 4) == 1 { return "image/jpeg" as *u8 }
236 if hr_ieq(ext, ".jpeg" as *u8, 5) == 1 { return "image/jpeg" as *u8 }
237 if hr_ieq(ext, ".svg" as *u8, 4) == 1 { return "image/svg+xml" as *u8 }
238 if hr_ieq(ext, ".gif" as *u8, 4) == 1 { return "image/gif" as *u8 }
239 if hr_ieq(ext, ".txt" as *u8, 4) == 1 { return "text/plain; charset=utf-8" as *u8 }
240 if hr_ieq(ext, ".ico" as *u8, 4) == 1 { return "image/x-icon" as *u8 }
241 if hr_ieq(ext, ".pdf" as *u8, 4) == 1 { return "application/pdf" as *u8 }
242 if hr_ieq(ext, ".webmanifest" as *u8, 12) == 1 { return "application/manifest+json" as *u8 }
243 if hr_ieq(ext, ".wasm" as *u8, 5) == 1 { return "application/wasm" as *u8 }
244 if hr_ieq(ext, ".mp4" as *u8, 4) == 1 { return "video/mp4" as *u8 }
245 if hr_ieq(ext, ".webm" as *u8, 5) == 1 { return "video/webm" as *u8 }
246 if hr_ieq(ext, ".woff2" as *u8, 6) == 1 { return "font/woff2" as *u8 }
247 // interop artifacts (the office/coordination second-half): calendar subscribe + office docs
248 if hr_ieq(ext, ".ics" as *u8, 4) == 1 { return "text/calendar; charset=utf-8" as *u8 }
249 if hr_ieq(ext, ".docx" as *u8, 5) == 1 { return "application/vnd.openxmlformats-officedocument.wordprocessingml.document" as *u8 }
250 if hr_ieq(ext, ".xlsx" as *u8, 5) == 1 { return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" as *u8 }
251 if hr_ieq(ext, ".pptx" as *u8, 5) == 1 { return "application/vnd.openxmlformats-officedocument.presentationml.presentation" as *u8 }
252 if hr_ieq(ext, ".odt" as *u8, 4) == 1 { return "application/vnd.oasis.opendocument.text" as *u8 }
253 if hr_ieq(ext, ".eml" as *u8, 4) == 1 { return "message/rfc822" as *u8 }
254 return "application/octet-stream" as *u8
255}
256
257// Emit a full HTTP/1.1 response (status line + Content-Type + Content-Length + body) into out.
258func hr_emit(out: *u8, statusline: *u8, ctype: *u8, body: *u8, bodyn: i64) -> i64 {
259 var j: i64 = 0
260 var k: i64 = 0
261 while statusline[k] != (0 as u8) { out[j] = statusline[k]; j = j + 1; k = k + 1 }
262 let h1: *u8 = "\r\nContent-Type: " as *u8
263 k = 0; while h1[k] != (0 as u8) { out[j] = h1[k]; j = j + 1; k = k + 1 }
264 k = 0; while ctype[k] != (0 as u8) { out[j] = ctype[k]; j = j + 1; k = k + 1 }
265 let h2: *u8 = "\r\nContent-Length: " as *u8
266 k = 0; while h2[k] != (0 as u8) { out[j] = h2[k]; j = j + 1; k = k + 1 }
267 j = j + hr_putdec(((out as i64 + j) as *u8), bodyn)
268 let h3: *u8 = "\r\nConnection: close\r\nX-Served-By: nishi-host\r\n\r\n" as *u8
269 k = 0; while h3[k] != (0 as u8) { out[j] = h3[k]; j = j + 1; k = k + 1 }
270 var b: i64 = 0
271 while b < bodyn { out[j] = body[b]; j = j + 1; b = b + 1 }
272 return j
273}
274
275// Does the request's Host match a configured site? Lets the daemon route ONLY config hosts
276// through the file server and fall back to its legacy routing (e.g. the wiki) for the rest.
277func hr_known_host(cfg: *u8, cfgn: i64, req: *u8, reqn: i64) -> i64 {
278 if cfgn <= 0 { return 0 }
279 let host: *u8 = sys_mmap(256)
280 let root: *u8 = sys_mmap(HR_MAGIC_1024)
281 let hn: i64 = hr_req_host(req, reqn, host, 256)
282 if hr_lookup_root(cfg, cfgn, host, hn, root, HR_MAGIC_1024) > 0 { return 1 }
283 return 0
284}
285
286// Full pipeline: request bytes + config -> HTTP response bytes in out. Reads the file PER REQUEST
287// (hot content). outcap must hold headers + file. Returns response length.
288func hr_serve(cfg: *u8, cfgn: i64, req: *u8, reqn: i64, out: *u8, outcap: i64) -> i64 {
289 let host: *u8 = sys_mmap(256)
290 let path: *u8 = sys_mmap(HR_MAGIC_4096)
291 let root: *u8 = sys_mmap(HR_MAGIC_1024)
292 let file: *u8 = sys_mmap(HR_MAGIC_5120)
293 let hn: i64 = hr_req_host(req, reqn, host, 256)
294 let pn: i64 = hr_req_path(req, reqn, path, HR_MAGIC_4096)
295 let rn: i64 = hr_lookup_root(cfg, cfgn, host, hn, root, HR_MAGIC_1024)
296 if rn == 0 {
297 let b0: *u8 = "<!doctype html><meta charset=utf-8><title>404</title><h1>404 — unknown host</h1>" as *u8
298 return hr_emit(out, "HTTP/1.1 404 Not Found" as *u8, "text/html; charset=utf-8" as *u8, b0, hr_slen(b0))
299 }
300 if hr_path_safe(path, pn) == 0 {
301 let b1: *u8 = "<!doctype html><meta charset=utf-8><title>400</title><h1>400 — bad path</h1>" as *u8
302 return hr_emit(out, "HTTP/1.1 400 Bad Request" as *u8, "text/html; charset=utf-8" as *u8, b1, hr_slen(b1))
303 }
304 let fnlen: i64 = hr_resolve(root, rn, path, pn, file, HR_MAGIC_5120)
305 let lenbox: *i64 = (sys_mmap(8)) as *i64
306 lenbox[0] = 0
307 let data: *u8 = sys_read_file(file, lenbox)
308 if (data as i64) == 0 {
309 let b2: *u8 = "<!doctype html><meta charset=utf-8><title>404</title><h1>404 — not found</h1>" as *u8
310 return hr_emit(out, "HTTP/1.1 404 Not Found" as *u8, "text/html; charset=utf-8" as *u8, b2, hr_slen(b2))
311 }
312 let dn: i64 = lenbox[0]
313 let ct: *u8 = hr_ctype(file, fnlen)
314 return hr_emit(out, "HTTP/1.1 200 OK" as *u8, ct, data, dn)
315}
316
317// ===== v2: bounds-checked, keep-alive, fallthrough-aware serving =====
318// (ADDITIVE -- hr_serve/hr_emit above are unchanged; the deployed daemon's
319// behaviour is bit-identical until it opts into hr_serve2.)
320
321// Bounds-checked emit. Writes status + Content-Type + Content-Length +
322// keep-alive headers + body into out; returns total length, or -1 if it
323// would not fit in outcap (caller turns that into a 500). Keep-alive (not
324// close) so one TLS handshake serves the whole page's assets.
325// Asset cache window. HTML is NEVER cached (the slot rotates on a 60s window and the served-impression
326// counter rides the HTML, so a cached page would freeze both rotation and billing); an asset is a
327// different fact. Bounded rather than immutable because house creatives sit at STABLE urls -- anything
328// uploaded through nx_adnet_creative is content-addressed and could safely take far longer.
329// DECLARED ABOVE ITS READER ON PURPOSE: nx_parse refuses a forward const read rather than silently
330// letting it evaluate to 0, which would have emitted "max-age=0" and looked like it worked.
331const HR_ASSET_MAXAGE: i64 = 3600
332
333// gzip negotiation helper. MEASURED 2026-08-08: NishiLang has NO module-level mutable state -- a
334// `var` at module scope is a STATEMENT AT MODULE LEVEL and FAILS THE BUILD (nx_cc, line 27904).
335// So the flag-in-a-static design is impossible; the gzip bit must be threaded as a PARAMETER.
336func hr_ae_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
337func hr_accepts_gzip(req: *u8, reqn: i64) -> i64 {
338 let key: *u8 = "accept-encoding:" as *u8
339 var i: i64 = 0
340 while i + 16 <= reqn {
341 var j: i64 = 0
342 var ok: i64 = 1
343 while j < 16 { if hr_ae_lc(req[i+j] as i64) != (key[j] as i64) { ok = 0; break } j = j + 1 }
344 if ok == 1 {
345 var e: i64 = i + 16
346 while e + 4 <= reqn {
347 if req[e] == (13 as u8) { return 0 }
348 if hr_ae_lc(req[e] as i64) == 103 { if hr_ae_lc(req[e+1] as i64) == 122 { if hr_ae_lc(req[e+2] as i64) == 105 { if hr_ae_lc(req[e+3] as i64) == 112 { return 1 } } } }
349 e = e + 1
350 }
351 return 0
352 }
353 i = i + 1
354 }
355 return 0
356}
357
358// Wrapper: keeps all 8 existing call sites byte-identical (gz=0). Forward ref to hr_emit_bz is
359// legal -- PROVEN by nx_fwdref_probe, not assumed.
360func hr_emit_b(out: *u8, outcap: i64, statusline: *u8, ctype: *u8, body: *u8, bodyn: i64) -> i64 {
361 return hr_emit_bz(out, outcap, statusline, ctype, body, bodyn, 0)
362}
363
364func hr_emit_bz(out: *u8, outcap: i64, statusline: *u8, ctype: *u8, body: *u8, bodyn: i64, gz: i64) -> i64 {
365 // ZERO-ALLOCATION: scratch is a slice of the caller's OWN out buffer, never a per-request
366 // sys_mmap -- an mmap here is the leak class fixed in sd2_build_resp today, 30x larger.
367 // Guards are conservative so the fallback is always the untouched identity path.
368 var eb: *u8 = body
369 var ebn: i64 = bodyn
370 var egz: i64 = 0
371 // Scratch needs room for the OUTPUT only, not 2n: `body` is sys_read_file's own allocation and
372 // provably cannot alias `out`, so there is no in-place overlap to defend against. Worst-case
373 // gzip growth on incompressible input is ~5B per 64KB stored block + 18B header/trailer, so
374 // bodyn+8192 bounds it. This drops the entry guard from 3n to ~n, which is what lets a 4.4MB
375 // page qualify at all. The fit is then RE-CHECKED against the real compressed length and falls
376 // back to identity if it would collide -- `body` is untouched, so the fallback is always safe.
377 if gz == 1 { if bodyn > HR_MAGIC_1024 { if bodyn + HR_MAGIC_9216 < outcap {
378 let sc: *u8 = ((out as i64) + (outcap - (bodyn + HR_MAGIC_8192))) as *u8
379 let cl: i64 = gz_compress(body, bodyn, 2, sc)
380 if cl > 0 { if cl < bodyn { if cl + HR_MAGIC_1024 < outcap - (bodyn + HR_MAGIC_8192) { eb = sc; ebn = cl; egz = 1 } } }
381 } } }
382 // headers are < 800 bytes for any status/ctype string (CORS + long office ctypes + the SOTA security header set: CSP/HSTS/nosniff/X-Frame-Options/Referrer-Policy)
383 // ROOT FIX (2026-07-31, debt 1785513321). This was a BLANKET RESERVE: `bodyn + HR_MAGIC_1024 > outcap`,
384 // which for outcap=1024 is true for ANY non-empty body -- so hr_serve2's TOOBIG branch could never emit
385 // its own 500 and an oversize file became a SILENT CONNECTION DROP instead of an error. A guard that
386 // cannot be satisfied produces a bypass, not safety. nx_host_router2_test asserted the 500 and had been
387 // RED on that tooth, uncompiled and unnoticed.
388 // Now bounded PRECISELY: refuse an outcap too small to hold the header block at all, then check the
389 // ACTUAL accumulated header length against the body before writing a single body byte.
390 if outcap < HR_MAGIC_1024 { return 0 - 1 }
391 var j: i64 = 0
392 var k: i64 = 0
393 while statusline[k] != (0 as u8) { out[j] = statusline[k]; j = j + 1; k = k + 1 }
394 let h1: *u8 = "\r\nContent-Type: " as *u8
395 k = 0; while h1[k] != (0 as u8) { out[j] = h1[k]; j = j + 1; k = k + 1 }
396 k = 0; while ctype[k] != (0 as u8) { out[j] = ctype[k]; j = j + 1; k = k + 1 }
397 let h2: *u8 = "\r\nContent-Length: " as *u8
398 k = 0; while h2[k] != (0 as u8) { out[j] = h2[k]; j = j + 1; k = k + 1 }
399 j = j + hr_putdec(((out as i64 + j) as *u8), ebn)
400 if egz == 1 {
401 let hg: *u8 = "\r\nContent-Encoding: gzip\r\nVary: Accept-Encoding" as *u8
402 k = 0; while hg[k] != (0 as u8) { out[j] = hg[k]; j = j + 1; k = k + 1 }
403 }
404 // F1120 CROSS-ORIGIN ISOLATION (2026-07-28; /video/capprobe MEASURED crossOriginIsolated=false as
405 // the R1 blocker): COOP same-origin + COEP require-corp are what let a page use SharedArrayBuffer =
406 // wasm THREADS = tile-parallel sovereign encode (the 60fps+/4K multiplier). BLAST RADIUS NEAR-ZERO
407 // BY CONSTRUCTION: the CSP here is already default-src 'self', so every subresource is same-origin
408 // and CORP: same-origin covers them. A HEADER CHANGE KILLED THIS PRODUCT FOR 5 DAYS (seq1073) --
409 // the video_canary tooth asserts these tokens too: a regression fails the same 300s pass it ships in.
410 // CACHEABILITY BY CONTENT TYPE (2026-07-31). This emitter hardcoded no-cache for EVERY response,
411 // images included, so a creative shipped on every served page was re-downloaded every single time.
412 // HTML MUST STAY no-cache: the slot rotates on a 60s window and the served-impression counter rides
413 // the HTML, so caching a page would freeze both rotation and billing. An asset is a different fact.
414 let hA: *u8 = "\r\nConnection: keep-alive\r\nKeep-Alive: timeout=65\r\nCache-Control: " as *u8
415 k = 0; while hA[k] != (0 as u8) { out[j] = hA[k]; j = j + 1; k = k + 1 }
416 var hr_is_html: i64 = 1
417 let hr_hm: *u8 = "text/html" as *u8
418 var hq: i64 = 0
419 while hq < 9 { if ctype[hq] != hr_hm[hq] { hr_is_html = 0; break } hq = hq + 1 }
420 // CACHE ONLY A SUCCESS (2026-09-02, debt 1788361379). A 4xx/5xx body is never an asset, whatever its
421 // content type. MEASURED on the live edge: the problem+json 503 emitted for a dead /search backend carried
422 // `public, max-age=3600` because this rule keyed on the ctype alone, so every JSON/fetch client was told
423 // to cache a sixteen-hour outage an hour at a time -- and it fired again on a cold query that overran the
424 // edge window minutes after the backend came back. The status line is already in hand: read its first
425 // digit (statusline is always "HTTP/1.1 <code> ...", so [9] is the hundreds digit) and cache only 2xx.
426 var hr_cacheable: i64 = 0
427 if hr_is_html == 0 { if statusline[9] == (50 as u8) { hr_cacheable = 1 } }
428 if hr_cacheable == 0 {
429 let hc1: *u8 = "no-cache" as *u8
430 k = 0; while hc1[k] != (0 as u8) { out[j] = hc1[k]; j = j + 1; k = k + 1 }
431 } else {
432 let hc2: *u8 = "public, max-age=" as *u8
433 k = 0; while hc2[k] != (0 as u8) { out[j] = hc2[k]; j = j + 1; k = k + 1 }
434 j = j + hr_putdec(((out as i64 + j) as *u8), HR_ASSET_MAXAGE)
435 }
436 let h3: *u8 = "\r\nAccess-Control-Allow-Origin: *\r\nX-Content-Type-Options: nosniff\r\nX-Frame-Options: SAMEORIGIN\r\nReferrer-Policy: strict-origin-when-cross-origin\r\nStrict-Transport-Security: max-age=63072000; includeSubDomains\r\nCross-Origin-Opener-Policy: same-origin\r\nCross-Origin-Embedder-Policy: require-corp\r\nCross-Origin-Resource-Policy: same-origin\r\nContent-Security-Policy: default-src 'self'; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' blob:; worker-src 'self' blob:; font-src 'self' data:; connect-src 'self' http://127.0.0.1:7862; media-src 'self' blob:; frame-ancestors 'self'; base-uri 'none'; object-src 'none'\r\nX-Served-By: nishi-substrate-v2\r\n\r\n" as *u8
437 k = 0; while h3[k] != (0 as u8) { out[j] = h3[k]; j = j + 1; k = k + 1 }
438 // MEASURED bound: j is the ACTUAL header length just emitted, not a guessed reserve. Checked before a
439 // single body byte is written, so the refusal is exact and the TOOBIG path can emit its own 500.
440 if j + ebn > outcap { return 0 - 1 }
441 var b: i64 = 0
442 while b < ebn { out[j] = eb[b]; j = j + 1; b = b + 1 }
443 return j
444}
445
446// Serve verdicts for hr_serve2 (sealed enum).
447const HR_S2_MISS: i64 = 0 // host known but file absent -> caller falls through to legacy routing
448const HR_S2_OK: i64 = 1 // 200 written
449const HR_S2_BAD: i64 = 2 // 400 written (traversal/malformed)
450const HR_S2_TOOBIG: i64 = 3 // 500 written (file exceeds outcap)
451
452// v2 pipeline: like hr_serve but (a) bounds-checked emit, (b) keep-alive,
453// (c) MISS verdict instead of a baked 404 when the file is absent -- the
454// daemon decides per host whether to fall through to its legacy routing
455// (nishifamily wiki) or emit a real 404 (andelinwest). Response length is
456// written to *out_n; the verdict is the return value.
457func hr_serve2(cfg: *u8, cfgn: i64, req: *u8, reqn: i64, out: *u8, outcap: i64, out_n: *i64) -> i64 {
458 out_n[0] = 0
459 let host: *u8 = sys_mmap(256)
460 let path: *u8 = sys_mmap(HR_MAGIC_4096)
461 let root: *u8 = sys_mmap(HR_MAGIC_1024)
462 let file: *u8 = sys_mmap(HR_MAGIC_5120)
463 let hn: i64 = hr_req_host(req, reqn, host, 256)
464 let pn: i64 = hr_req_path(req, reqn, path, HR_MAGIC_4096)
465 let rn: i64 = hr_lookup_root(cfg, cfgn, host, hn, root, HR_MAGIC_1024)
466 if rn == 0 { return HR_S2_MISS }
467 if hr_path_safe(path, pn) == 0 {
468 let b1: *u8 = "<!doctype html><meta charset=utf-8><title>400</title><h1>400 — bad path</h1>" as *u8
469 out_n[0] = hr_emit_b(out, outcap, "HTTP/1.1 400 Bad Request" as *u8, "text/html; charset=utf-8" as *u8, b1, hr_slen(b1))
470 return HR_S2_BAD
471 }
472 let fnlen: i64 = hr_resolve(root, rn, path, pn, file, HR_MAGIC_5120)
473 let lenbox: *i64 = (sys_mmap(8)) as *i64
474 lenbox[0] = 0
475 let data: *u8 = sys_read_file(file, lenbox)
476 if (data as i64) == 0 { return HR_S2_MISS }
477 let dn: i64 = lenbox[0]
478 let ct: *u8 = hr_ctype(file, fnlen)
479 let w: i64 = hr_emit_bz(out, outcap, "HTTP/1.1 200 OK" as *u8, ct, data, dn, hr_accepts_gzip(req, reqn))
480 if w < 0 {
481 let b3: *u8 = "<!doctype html><meta charset=utf-8><title>500</title><h1>500 — file exceeds serve buffer</h1>" as *u8
482 out_n[0] = hr_emit_b(out, outcap, "HTTP/1.1 500 Internal Server Error" as *u8, "text/html; charset=utf-8" as *u8, b3, hr_slen(b3))
483 return HR_S2_TOOBIG
484 }
485 out_n[0] = w
486 return HR_S2_OK
487}
488
489// Real 404 for hosts that do NOT fall through (e.g. andelinwest.com).
490func hr_emit_404(out: *u8, outcap: i64) -> i64 {
491 let b2: *u8 = "<!doctype html><meta charset=utf-8><title>404</title><h1>404 — not found</h1>" as *u8
492 return hr_emit_b(out, outcap, "HTTP/1.1 404 Not Found" as *u8, "text/html; charset=utf-8" as *u8, b2, hr_slen(b2))
493}
494
495// ===== v3: S-CLASS ROUTING -- clean URLs + canonical 301s, ONE resolver for every path =====
496// (ADDITIVE: hr_serve/hr_serve2 above are byte-identical and untouched.) This mirrors the gated pure
497// engine nx_route.nx (nx_route_gate 5/5) against the LIVE filesystem so routing is a property of the
498// HOST, not per-page redirect stubs:
499// * CLEAN extensionless URLs serve, for BOTH layouts: /foo -> foo/index.html (dir) OR foo.html (flat).
500// * ONE CANONICAL url via REAL 301s: /foo.html, /foo/, /foo/index.html, /index.html all collapse to
501// the clean canonical -- BUT only when that clean target actually resolves to a file, so a legacy
502// .html route (served by fallthrough, not a docroot file) is never 301'd into a 404.
503// * dir-index keeps PRECEDENCE over .html (so an existing /games hub is never shadowed by a stale flat).
504// * traversal -> 400 ; nothing resolves -> MISS (caller falls through to legacy, unchanged).
505const HR_S2_REDIR: i64 = 4 // 301 written to out (out_n set); caller must NOT fall through
506const HR_S2_STREAM: i64 = 5 // headers written to out; BODY handed via hr_stream_body/bodyn -- the
507 // caller sends both through its own chunked TLS primitive. ZERO-CEILING
508 // static serving (debt 1785879638): no file-size constant exists on this
509 // path; anything the host can mmap streams. Body ptr/len ride module
510 // statics (scalars, not arrays -- the BSS-array crash class): the daemon
511 // forks per connection, so the pair is per-process and consumed before
512 // the next request in that process.
513static hr_stream_p: i64 = 0
514static hr_stream_n: i64 = 0
515func hr_stream_body() -> i64 { return hr_stream_p }
516func hr_stream_bodyn() -> i64 { return hr_stream_n }
517
518// header half of hr_emit_b, byte-identical headers (incl cacheability-by-ctype + the security set);
519// Content-Length = bodyn, but NO body byte is written -- the stream caller sends the body itself.
520func hr_decimal_len(v: i64) -> i64 { var x: i64=v;var n: i64=1;while x>=10 { n=n+1;x=x/10 };return n }
521func hr_emit_head(out: *u8, outcap: i64, statusline: *u8, ctype: *u8, bodyn: i64) -> i64 {
522 if bodyn<0 { return 0-1 }
523 let h1: *u8 = "\r\nContent-Type: " as *u8
524 let h2: *u8 = "\r\nContent-Length: " as *u8
525 let hA: *u8 = "\r\nConnection: keep-alive\r\nKeep-Alive: timeout=65\r\nCache-Control: " as *u8
526 let h3: *u8 = "\r\nAccess-Control-Allow-Origin: *\r\nX-Content-Type-Options: nosniff\r\nX-Frame-Options: SAMEORIGIN\r\nReferrer-Policy: strict-origin-when-cross-origin\r\nStrict-Transport-Security: max-age=63072000; includeSubDomains\r\nCross-Origin-Opener-Policy: same-origin\r\nCross-Origin-Embedder-Policy: require-corp\r\nCross-Origin-Resource-Policy: same-origin\r\nContent-Security-Policy: default-src 'self'; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' blob:; worker-src 'self' blob:; font-src 'self' data:; connect-src 'self' http://127.0.0.1:7862; media-src 'self' blob:; frame-ancestors 'self'; base-uri 'none'; object-src 'none'\r\nX-Served-By: nishi-substrate-v2\r\n\r\n" as *u8
527 var cache: *u8="no-cache" as *u8
528 var cache_digits: i64=0
529 if hr_ct_is_html(ctype)==0 && statusline[9]==(50 as u8) {
530 cache="public, max-age=" as *u8;cache_digits=hr_decimal_len(HR_ASSET_MAXAGE)
531 }
532 let needed: i64=hr_slen(statusline)+hr_slen(h1)+hr_slen(ctype)+hr_slen(h2)+hr_decimal_len(bodyn)+hr_slen(hA)+hr_slen(cache)+cache_digits+hr_slen(h3)
533 if (out as i64)==0 { return needed }
534 if needed>outcap { return 0-1 }
535 var j: i64 = 0
536 var k: i64 = 0
537 while statusline[k] != (0 as u8) { out[j] = statusline[k]; j = j + 1; k = k + 1 }
538 k = 0; while h1[k] != (0 as u8) { out[j] = h1[k]; j = j + 1; k = k + 1 }
539 k = 0; while ctype[k] != (0 as u8) { out[j] = ctype[k]; j = j + 1; k = k + 1 }
540 k = 0; while h2[k] != (0 as u8) { out[j] = h2[k]; j = j + 1; k = k + 1 }
541 j = j + hr_putdec(((out as i64 + j) as *u8), bodyn)
542 k = 0; while hA[k] != (0 as u8) { out[j] = hA[k]; j = j + 1; k = k + 1 }
543 var hh: i64 = 1
544 let hm: *u8 = "text/html" as *u8
545 var hq: i64 = 0
546 while hq < 9 { if ctype[hq] != hm[hq] { hh = 0; break } hq = hq + 1 }
547 if hh == 1 || statusline[9]!=(50 as u8) {
548 let hc1: *u8 = "no-cache" as *u8
549 k = 0; while hc1[k] != (0 as u8) { out[j] = hc1[k]; j = j + 1; k = k + 1 }
550 } else {
551 let hc2: *u8 = "public, max-age=" as *u8
552 k = 0; while hc2[k] != (0 as u8) { out[j] = hc2[k]; j = j + 1; k = k + 1 }
553 j = j + hr_putdec(((out as i64 + j) as *u8), HR_ASSET_MAXAGE)
554 }
555 k = 0; while h3[k] != (0 as u8) { out[j] = h3[k]; j = j + 1; k = k + 1 }
556 if j > outcap { return 0 - 1 }
557 return j
558}
559
560func hr_ends2(s: *u8, n: i64, suf: *u8, sufn: i64) -> i64 {
561 if sufn > n { return 0 }
562 var i: i64 = 0
563 while i < sufn { if s[n - sufn + i] != suf[i] { return 0 } i = i + 1 }
564 return 1
565}
566
567// Canonical clean form of path -> loc (NUL-terminated). Returns locn (>0 => a redirect is warranted),
568// or 0 when the path is already canonical. Order: /index.html before .html (the former is a suffix of it).
569func hr_canon(path: *u8, pn: i64, loc: *u8) -> i64 {
570 if pn <= 1 { return 0 }
571 if hr_ends2(path, pn, "/index.html" as *u8, 11) == 1 {
572 let m1: i64 = pn - 11
573 if m1 <= 0 { loc[0] = 47 as u8; loc[1] = 0 as u8; return 1 }
574 var i1: i64 = 0; while i1 < m1 { loc[i1] = path[i1]; i1 = i1 + 1 } loc[m1] = 0 as u8; return m1
575 }
576 if hr_ends2(path, pn, ".html" as *u8, 5) == 1 {
577 let m2: i64 = pn - 5
578 if m2 <= 0 { return 0 }
579 var i2: i64 = 0; while i2 < m2 { loc[i2] = path[i2]; i2 = i2 + 1 } loc[m2] = 0 as u8; return m2
580 }
581 if path[pn - 1] == (47 as u8) {
582 let m3: i64 = pn - 1
583 var i3: i64 = 0; while i3 < m3 { loc[i3] = path[i3]; i3 = i3 + 1 } loc[m3] = 0 as u8; return m3
584 }
585 return 0
586}
587
588// Join root (trailing '/' trimmed) + path + suffix into file; return length.
589func hr_join(root: *u8, rn: i64, path: *u8, pn: i64, suffix: *u8, file: *u8, cap: i64) -> i64 {
590 var j: i64 = 0; var i: i64 = 0
591 while i < rn { if j < cap - 1 { file[j] = root[i]; j = j + 1 } i = i + 1 }
592 if j > 0 { if file[j - 1] == (47 as u8) { j = j - 1 } }
593 i = 0
594 while i < pn { if j < cap - 1 { file[j] = path[i]; j = j + 1 } i = i + 1 }
595 if (suffix as i64) != 0 {
596 i = 0
597 while suffix[i] != (0 as u8) { if j < cap - 1 { file[j] = suffix[i]; j = j + 1 } i = i + 1 }
598 }
599 file[j] = 0 as u8
600 return j
601}
602
603// Resolve path -> the first existing candidate file (read PER REQUEST = hot content). Candidate order:
604// "/" -> index.html ; dotted last segment (asset) -> exact ; clean URL -> dir-index THEN .html.
605// Returns data ptr (0 = nothing resolved); sets *fnlen (file-path length, for MIME) + *dn (byte count).
606func hr_resolve_read(root: *u8, rn: i64, path: *u8, pn: i64, file: *u8, cap: i64, fnlen: *i64, dn: *i64) -> *u8 {
607 let lenbox: *i64 = (sys_mmap(8)) as *i64; lenbox[0] = 0
608 var data: *u8 = 0 as *u8
609 if pn == 1 {
610 fnlen[0] = hr_join(root, rn, path, pn, "index.html" as *u8, file, cap)
611 data = sys_read_file(file, lenbox)
612 } else {
613 var seg_dot: i64 = 0; var s: i64 = pn - 1
614 while s >= 0 { if path[s] == (47 as u8) { break } if path[s] == (46 as u8) { seg_dot = 1; break } s = s - 1 }
615 if seg_dot == 1 {
616 fnlen[0] = hr_join(root, rn, path, pn, 0 as *u8, file, cap)
617 data = sys_read_file(file, lenbox)
618 } else {
619 fnlen[0] = hr_join(root, rn, path, pn, "/index.html" as *u8, file, cap)
620 data = sys_read_file(file, lenbox)
621 if (data as i64) == 0 {
622 fnlen[0] = hr_join(root, rn, path, pn, ".html" as *u8, file, cap)
623 data = sys_read_file(file, lenbox)
624 }
625 }
626 }
627 dn[0] = lenbox[0]
628 return data
629}
630
631// Emit a 301 with Location (empty body, keep-alive). -1 if it would not fit.
632func hr_emit_redirect(out: *u8, outcap: i64, loc: *u8, locn: i64) -> i64 {
633 if locn + 256 > outcap { return 0 - 1 }
634 var j: i64 = 0; var k: i64 = 0
635 let s1: *u8 = "HTTP/1.1 301 Moved Permanently\r\nLocation: " as *u8
636 k = 0; while s1[k] != (0 as u8) { out[j] = s1[k]; j = j + 1; k = k + 1 }
637 k = 0; while k < locn { out[j] = loc[k]; j = j + 1; k = k + 1 }
638 let s2: *u8 = "\r\nContent-Length: 0\r\nConnection: keep-alive\r\nCache-Control: no-cache\r\nX-Served-By: nishi-host\r\n\r\n" as *u8
639 k = 0; while s2[k] != (0 as u8) { out[j] = s2[k]; j = j + 1; k = k + 1 }
640 return j
641}
642
643// ===== v3+slot helpers: universal ad-slot injection (ADDITIVE -- hr_serve3 below is untouched) =====
644// hr_serve3_slot behaves byte-identically to hr_serve3 when slot_n==0 (gated). With slot bytes: a
645// text/html body containing </body> and NOT carrying the nx-ad-optout marker gets the slot inserted
646// before the LAST </body>; Content-Length stays correct by construction (merge happens before emit).
647// injected[0]=1 ONLY when the slot actually landed (the caller logs a served impression off it).
648
649func hr_ct_is_html(ct: *u8) -> i64 {
650 return hr_eq(ct, "text/html" as *u8, 9)
651}
652
653// index of the LAST "</body>" (case-insensitive) in body, or -1.
654func hr_find_close_body(body: *u8, bn: i64) -> i64 {
655 if bn < 7 { return 0 - 1 }
656 var i: i64 = bn - 7
657 while i >= 0 {
658 if body[i] == (60 as u8) {
659 if hr_ieq(((body as i64 + i) as *u8), "</body>" as *u8, 7) == 1 { return i }
660 }
661 i = i - 1
662 }
663 return 0 - 1
664}
665
666// page-level opt-out marker scan (any page may carry nx-ad-optout to refuse the slot).
667func hr_has_optout(body: *u8, bn: i64) -> i64 {
668 var i: i64 = 0
669 while i + 12 <= bn {
670 if body[i] == (110 as u8) {
671 if hr_eq(((body as i64 + i) as *u8), "nx-ad-optout" as *u8, 12) == 1 { return 1 }
672 }
673 i = i + 1
674 }
675 return 0
676}
677
678
679// ---- SLOT ANCHOR (2026-07-31): let a PAGE choose where its ad goes -------------------------------
680// WHY: the slot was always injected before the closing body tag -- the absolute bottom of the document.
681// On a long reference page essentially nobody scrolls there, and the MRC rule needs 50% of the creative
682// in view for one CONTINUOUS second. So viewable impressions were structurally near-zero: we measured
683// honestly and still sold a position nobody sees. The honest number does not fix placement, it EXPOSES
684// it -- and this is the mechanism that lets it be fixed.
685//
686// A page opts in by placing the marker where it wants the ad. NO MARKER = the old behaviour, byte for
687// byte, so this cannot change a single existing page. Placement stays a PRODUCT decision made per page,
688// not an engineering default imposed on every site at once.
689const HR_SLOT_MARK: *u8 = "<!--nx-ad-slot-->" as *u8
690
691func hr_find_slot_anchor(d: *u8, n: i64) -> i64 {
692 var i: i64 = 0
693 while i + 17 <= n {
694 var k: i64 = 0
695 var m: i64 = 1
696 while k < 17 { if d[i + k] != HR_SLOT_MARK[k] { m = 0; break } k = k + 1 }
697 if m == 1 { return i }
698 i = i + 1
699 }
700 return hr_find_close_body(d, n)
701}
702
703func hr_serve3_slot(cfg: *u8, cfgn: i64, req: *u8, reqn: i64, out: *u8, outcap: i64, out_n: *i64, slot: *u8, slot_n: i64, injected: *i64) -> i64 {
704 injected[0] = 0
705 out_n[0] = 0
706 let host: *u8 = sys_mmap(256)
707 let path: *u8 = sys_mmap(HR_MAGIC_4096)
708 let root: *u8 = sys_mmap(HR_MAGIC_1024)
709 let file: *u8 = sys_mmap(HR_MAGIC_5120)
710 let hn: i64 = hr_req_host(req, reqn, host, 256)
711 let pn: i64 = hr_req_path(req, reqn, path, HR_MAGIC_4096)
712 let rn: i64 = hr_lookup_root(cfg, cfgn, host, hn, root, HR_MAGIC_1024)
713 if rn == 0 { return HR_S2_MISS }
714 if hr_path_safe(path, pn) == 0 {
715 let b1: *u8 = "<!doctype html><meta charset=utf-8><title>400</title><h1>400 — bad path</h1>" as *u8
716 out_n[0] = hr_emit_b(out, outcap, "HTTP/1.1 400 Bad Request" as *u8, "text/html; charset=utf-8" as *u8, b1, hr_slen(b1))
717 return HR_S2_BAD
718 }
719 let ranged: i64=hr_try_file_range(root,rn,path,pn,req,reqn,out,outcap,out_n)
720 if ranged==HR_S2_FILE_STREAM {
721 sys_munmap(host,256);sys_munmap(path,HR_MAGIC_4096)
722 sys_munmap(root,HR_MAGIC_1024);sys_munmap(file,HR_MAGIC_5120)
723 return ranged
724 }
725 let fnlen: *i64 = (sys_mmap(8)) as *i64
726 let dn: *i64 = (sys_mmap(8)) as *i64
727 let loc: *u8 = sys_mmap(HR_MAGIC_4096)
728 let locn: i64 = hr_canon(path, pn, loc)
729 if locn > 0 {
730 let cfile: *u8 = sys_mmap(HR_MAGIC_5120)
731 let cdata: *u8 = hr_resolve_read(root, rn, loc, locn, cfile, HR_MAGIC_5120, fnlen, dn)
732 if (cdata as i64) != 0 {
733 // seq365: CARRY the ?query on the canonical 301 (resolve above used the PATH-only loc;
734 // append after resolve so filesystem lookup never sees query bytes). No-query requests
735 // emit byte-identical redirects to before.
736 let qbuf: *u8 = sys_mmap(HR_MAGIC_2048)
737 let qn: i64 = hr_req_query(req, reqn, qbuf, HR_MAGIC_2048)
738 var locq: i64 = locn
739 if qn > 0 { if locn + qn < HR_MAGIC_4095 {
740 var qi: i64 = 0
741 while qi < qn { loc[locn + qi] = qbuf[qi]; qi = qi + 1 }
742 locq = locn + qn
743 loc[locq] = 0 as u8
744 } }
745 out_n[0] = hr_emit_redirect(out, outcap, loc, locq)
746 if out_n[0] < 0 { out_n[0] = 0; return HR_S2_MISS }
747 return HR_S2_REDIR
748 }
749 }
750 let data: *u8 = hr_resolve_read(root, rn, path, pn, file, HR_MAGIC_5120, fnlen, dn)
751 if (data as i64) == 0 { return HR_S2_MISS }
752 let ct: *u8 = hr_ctype(file, fnlen[0])
753 var body: *u8 = data
754 var bodyn: i64 = dn[0]
755 if slot_n > 0 {
756 if hr_ct_is_html(ct) == 1 {
757 let cb: i64 = hr_find_slot_anchor(data, bodyn)
758 if cb >= 0 {
759 let oo: i64 = hr_has_optout(data, bodyn)
760 if oo == 0 {
761 let mcap: i64 = bodyn + slot_n + 16
762 let merged: *u8 = sys_mmap(mcap)
763 var w2: i64 = 0
764 var a: i64 = 0
765 while a < cb { merged[w2] = data[a]; w2 = w2 + 1; a = a + 1 }
766 var b2: i64 = 0
767 while b2 < slot_n { merged[w2] = slot[b2]; w2 = w2 + 1; b2 = b2 + 1 }
768 while a < bodyn { merged[w2] = data[a]; w2 = w2 + 1; a = a + 1 }
769 body = merged
770 bodyn = w2
771 injected[0] = 1
772 }
773 }
774 }
775 }
776 let w: i64 = hr_emit_bz(out, outcap, "HTTP/1.1 200 OK" as *u8, ct, body, bodyn, hr_accepts_gzip(req, reqn))
777 if w < 0 {
778 // ZERO-CEILING STREAM (debt 1785879638; operator: no self-imposed limits, no magic numbers):
779 // the body is ALREADY in memory -- emit headers only (they always fit any sane outcap) and
780 // hand the body pointer to the daemon, which sends both through the same chunked TLS send.
781 // No file-size constant exists on this path. 500 remains ONLY for an outcap below the header floor.
782 let hw: i64 = hr_emit_head(out, outcap, "HTTP/1.1 200 OK" as *u8, ct, bodyn)
783 if hw > 0 {
784 hr_stream_p = body as i64
785 hr_stream_n = bodyn
786 out_n[0] = hw
787 return HR_S2_STREAM
788 }
789 let b3: *u8 = "<!doctype html><meta charset=utf-8><title>500</title><h1>500 — file exceeds serve buffer</h1>" as *u8
790 out_n[0] = hr_emit_b(out, outcap, "HTTP/1.1 500 Internal Server Error" as *u8, "text/html; charset=utf-8" as *u8, b3, hr_slen(b3))
791 return HR_S2_TOOBIG
792 }
793 out_n[0] = w
794 return HR_S2_OK
795}
796
797// v3 pipeline: canonical 301 (safe) + clean multi-candidate resolve. Verdicts: OK/MISS/BAD/TOOBIG/REDIR.
798func hr_serve3(cfg: *u8, cfgn: i64, req: *u8, reqn: i64, out: *u8, outcap: i64, out_n: *i64) -> i64 {
799 out_n[0] = 0
800 let host: *u8 = sys_mmap(256)
801 let path: *u8 = sys_mmap(HR_MAGIC_4096)
802 let root: *u8 = sys_mmap(HR_MAGIC_1024)
803 let file: *u8 = sys_mmap(HR_MAGIC_5120)
804 let hn: i64 = hr_req_host(req, reqn, host, 256)
805 let pn: i64 = hr_req_path(req, reqn, path, HR_MAGIC_4096)
806 let rn: i64 = hr_lookup_root(cfg, cfgn, host, hn, root, HR_MAGIC_1024)
807 if rn == 0 { return HR_S2_MISS }
808 if hr_path_safe(path, pn) == 0 {
809 let b1: *u8 = "<!doctype html><meta charset=utf-8><title>400</title><h1>400 — bad path</h1>" as *u8
810 out_n[0] = hr_emit_b(out, outcap, "HTTP/1.1 400 Bad Request" as *u8, "text/html; charset=utf-8" as *u8, b1, hr_slen(b1))
811 return HR_S2_BAD
812 }
813 let fnlen: *i64 = (sys_mmap(8)) as *i64
814 let dn: *i64 = (sys_mmap(8)) as *i64
815 // canonical 301 -- only when the clean target actually resolves (never 301 to a 404)
816 let loc: *u8 = sys_mmap(HR_MAGIC_4096)
817 let locn: i64 = hr_canon(path, pn, loc)
818 if locn > 0 {
819 let cfile: *u8 = sys_mmap(HR_MAGIC_5120)
820 let cdata: *u8 = hr_resolve_read(root, rn, loc, locn, cfile, HR_MAGIC_5120, fnlen, dn)
821 if (cdata as i64) != 0 {
822 // seq365: CARRY the ?query on the canonical 301 (resolve above used the PATH-only loc;
823 // append after resolve so filesystem lookup never sees query bytes). No-query requests
824 // emit byte-identical redirects to before.
825 let qbuf: *u8 = sys_mmap(HR_MAGIC_2048)
826 let qn: i64 = hr_req_query(req, reqn, qbuf, HR_MAGIC_2048)
827 var locq: i64 = locn
828 if qn > 0 { if locn + qn < HR_MAGIC_4095 {
829 var qi: i64 = 0
830 while qi < qn { loc[locn + qi] = qbuf[qi]; qi = qi + 1 }
831 locq = locn + qn
832 loc[locq] = 0 as u8
833 } }
834 out_n[0] = hr_emit_redirect(out, outcap, loc, locq)
835 if out_n[0] < 0 { out_n[0] = 0; return HR_S2_MISS }
836 return HR_S2_REDIR
837 }
838 }
839 // resolve + serve the (canonical) path
840 let data: *u8 = hr_resolve_read(root, rn, path, pn, file, HR_MAGIC_5120, fnlen, dn)
841 if (data as i64) == 0 { return HR_S2_MISS }
842 let ct: *u8 = hr_ctype(file, fnlen[0])
843 let w: i64 = hr_emit_b(out, outcap, "HTTP/1.1 200 OK" as *u8, ct, data, dn[0])
844 if w < 0 {
845 let b3: *u8 = "<!doctype html><meta charset=utf-8><title>500</title><h1>500 — file exceeds serve buffer</h1>" as *u8
846 out_n[0] = hr_emit_b(out, outcap, "HTTP/1.1 500 Internal Server Error" as *u8, "text/html; charset=utf-8" as *u8, b3, hr_slen(b3))
847 return HR_S2_TOOBIG
848 }
849 out_n[0] = w
850 return HR_S2_OK
851}
852static hr_file_reader: *NxFileReadRegion
853func hr_take_file_reader() -> *NxFileReadRegion {
854 let r: *NxFileReadRegion=hr_file_reader;hr_file_reader=0 as *NxFileReadRegion;return r
855}
856// Returns value offset, -1 absent, -2 duplicate. Only header lines are inspected.
857func hr_field(req: *u8,n: i64,name: *u8,len: *i64) -> i64 {
858 let nn: i64=hr_slen(name);var p: i64=0;var found: i64=0-1;len[0]=0
859 while p+1<n { if req[p]==(13 as u8) && req[p+1]==(10 as u8) { p=p+2;break };p=p+1 }
860 while p+1<n {
861 let start: i64=p
862 while p+1<n { if req[p]==(13 as u8) && req[p+1]==(10 as u8) { break };p=p+1 }
863 if p+1>=n || p==start { break }
864 if p-start>nn {
865 if hr_ieq(req+start,name,nn)==1 && req[start+nn]==(58 as u8) {
866 if found>=0 { return 0-2 }
867 var vs: i64=start+nn+1;var ve: i64=p
868 while vs<ve { if rh_ows(req[vs] as i64)==0 { break };vs=vs+1 }
869 while ve>vs { if rh_ows(req[ve-1] as i64)==0 { break };ve=ve-1 }
870 found=vs;len[0]=ve-vs
871 }
872 }
873 p=p+2
874 }
875 return found
876}
877func hr_emit_range_head(out: *u8,cap: i64,ct: *u8,r: *NxFileReadRegion,unsat: i64) -> i64 {
878 let prefix: *u8="Accept-Ranges: bytes\r\nContent-Range: bytes " as *u8
879 var extra: i64=hr_slen(prefix)+hr_decimal_len(r.total)+hr_slen("/\r\n\r\n" as *u8)
880 if unsat==1 { extra=extra+1 } else { extra=extra+hr_decimal_len(r.start)+hr_decimal_len(r.start+r.length-1)+1 }
881 var status: *u8="HTTP/1.1 206 Partial Content" as *u8
882 var bodylen: i64=r.length
883 if unsat==1 { status="HTTP/1.1 416 Range Not Satisfiable" as *u8;bodylen=0 }
884 let base: i64=hr_emit_head(out,cap-extra+2,status,ct,bodylen)
885 if base<0 { return base }
886 if (out as i64)==0 { return base-2+extra }
887 // Replace only the final empty line; all common headers stay owned by hr_emit_head.
888 var p: i64=base-2
889 p=p+hr_copy(out+p,prefix,hr_slen(prefix))
890 if unsat==1 { out[p]=42 as u8;p=p+1 } else {
891 p=p+hr_putdec(out+p,r.start);out[p]=45 as u8;p=p+1
892 p=p+hr_putdec(out+p,r.start+r.length-1)
893 }
894 out[p]=47 as u8;p=p+1;p=p+hr_putdec(out+p,r.total)
895 p=p+hr_copy(out+p,"\r\n\r\n" as *u8,4)
896 return p
897}
898// Range optimization for untransformed, explicit static-file paths. Unsupported
899// multi-range/conditional requests retain the preexisting full-response path.
900func hr_try_file_range(root: *u8,rn: i64,path: *u8,pn: i64,req: *u8,reqn: i64,out: *u8,cap: i64,outn: *i64) -> i64 {
901 if reqn<4 { return HR_S2_MISS }
902 if hr_eq(req,"GET " as *u8,4)==0 { return HR_S2_MISS }
903 let span: *i64=sys_mmap(__size_of(i64)) as *i64
904 let conditional: i64=hr_field(req,reqn,"If-Range",span)
905 let at: i64=hr_field(req,reqn,"Range",span);let len: i64=span[0]
906 sys_munmap(span as *u8,__size_of(i64))
907 if conditional!=(0-1) || at<0 { return HR_S2_MISS }
908 var dot: i64=0;var i: i64=pn
909 while i>0 { i=i-1;if path[i]==(47 as u8) { break };if path[i]==(46 as u8) { dot=1 } }
910 if dot==0 { return HR_S2_MISS }
911 let filecap: i64=rn+pn+2;let file: *u8=sys_mmap(filecap)
912 let fn: i64=hr_join(root,rn,path,pn,0 as *u8,file,filecap)
913 if fn<=0 { sys_munmap(file,filecap);return HR_S2_MISS }
914 let ct: *u8=hr_ctype(file,fn)
915 if hr_ct_is_html(ct)==1 { sys_munmap(file,filecap);return HR_S2_MISS }
916 let r: *NxFileReadRegion=sys_mmap(__size_of(NxFileReadRegion)) as *NxFileReadRegion
917 fio_region_init(r);let opened: i64=fio_region_open(file,r);sys_munmap(file,filecap)
918 if opened<0 { sys_munmap(r as *u8,__size_of(NxFileReadRegion));return HR_S2_MISS }
919 let range: *RangeSpec=sys_mmap(__size_of(RangeSpec)) as *RangeSpec
920 let parsed: i64=range_parse(req+at,len,r.total,range,1)
921 var selected: i64=0
922 if parsed==1 { selected=fio_region_select(r,range.start,range.end-range.start+1) }
923 if parsed==RH_ERR_UNSATISFIABLE { selected=fio_region_select(r,0,0) }
924 sys_munmap(range as *u8,__size_of(RangeSpec))
925 if selected<0 || (parsed!=1 && parsed!=RH_ERR_UNSATISFIABLE) {
926 fio_region_close(r);sys_munmap(r as *u8,__size_of(NxFileReadRegion));return HR_S2_MISS
927 }
928 let headers: i64=hr_emit_range_head(out,cap,ct,r,(parsed==RH_ERR_UNSATISFIABLE) as i64)
929 if headers<0 { fio_region_close(r);sys_munmap(r as *u8,__size_of(NxFileReadRegion));return HR_S2_MISS }
930 hr_file_reader=r;outn[0]=headers;return HR_S2_FILE_STREAM
931}