code wiki / _hdl_build / nx_host_router.nx
nx_host_router.nx source
↩ module page · 764 lines · 40120 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"
7const HR_MAGIC_1024: i64 = 1024
8const HR_MAGIC_4096: i64 = 4096
9const HR_MAGIC_5120: i64 = 5120
10const HR_MAGIC_2048: i64 = 2048
11const HR_MAGIC_4095: i64 = 4095
12
13func hr_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
14
15func 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 }
16
17func hr_eq(a: *u8, b: *u8, n: i64) -> i64 {
18 var i: i64 = 0
19 while i < n { if a[i] != b[i] { return 0 } i = i + 1 }
20 return 1
21}
22
23func hr_lower(c: u8) -> u8 {
24 if c >= (65 as u8) { if c <= (90 as u8) { return (c + (32 as u8)) } }
25 return c
26}
27
28func hr_ieq(a: *u8, b: *u8, n: i64) -> i64 {
29 var i: i64 = 0
30 while i < n { if hr_lower(a[i]) != hr_lower(b[i]) { return 0 } i = i + 1 }
31 return 1
32}
33
34// Decimal-render v into out; return digit count (no modulo operator dependency).
35func hr_putdec(out: *u8, v: i64) -> i64 {
36 if v == 0 { out[0] = (48 as u8); return 1 }
37 let tmp: *u8 = sys_mmap(32)
38 var n: i64 = 0
39 var x: i64 = v
40 while x > 0 {
41 let q: i64 = x / 10
42 let r: i64 = x - q * 10
43 tmp[n] = ((48 + r) as u8)
44 n = n + 1
45 x = q
46 }
47 var i: i64 = 0
48 while i < n { out[i] = tmp[n - 1 - i]; i = i + 1 }
49 return n
50}
51
52// Extract the request path ("GET <path> HTTP/1.1") into out (NUL-terminated); drop any ?query.
53func hr_req_path(req: *u8, reqn: i64, out: *u8, cap: i64) -> i64 {
54 var i: i64 = 0
55 while i < reqn { if req[i] == (32 as u8) { break } i = i + 1 }
56 i = i + 1
57 var j: i64 = 0
58 while i < reqn {
59 let c: u8 = req[i]
60 if c == (32 as u8) { break }
61 if c == (63 as u8) { break }
62 if j < cap - 1 { out[j] = c; j = j + 1 }
63 i = i + 1
64 }
65 out[j] = (0 as u8)
66 return j
67}
68
69// Extract the ?query of the request line INCLUDING the leading '?' (empty -> 0). seq365: the canonical
70// 301 used to rebuild Location from the PATH alone, silently dropping ?room=&k=... from parameterized
71// links (family invites, probe URLs). Redirects must CARRY the query; this is its one extractor.
72func hr_req_query(req: *u8, reqn: i64, out: *u8, cap: i64) -> i64 {
73 var i: i64 = 0
74 while i < reqn { if req[i] == (32 as u8) { break } i = i + 1 }
75 i = i + 1
76 var q: i64 = 0 - 1
77 while i < reqn {
78 let c: u8 = req[i]
79 if c == (32 as u8) { break }
80 if c == (63 as u8) { q = i; break }
81 i = i + 1
82 }
83 if q < 0 { out[0] = (0 as u8); return 0 }
84 var j: i64 = 0
85 while q < reqn {
86 let c2: u8 = req[q]
87 if c2 == (32 as u8) { break }
88 if c2 == (13 as u8) { break }
89 if c2 == (10 as u8) { break }
90 if j < cap - 1 { out[j] = c2; j = j + 1 }
91 q = q + 1
92 }
93 out[j] = (0 as u8)
94 return j
95}
96
97// Extract the Host header (lowercased, port stripped) into out (NUL-terminated).
98func hr_req_host(req: *u8, reqn: i64, out: *u8, cap: i64) -> i64 {
99 var i: i64 = 0
100 var found: i64 = 0 - 1
101 while i + 5 <= reqn {
102 if hr_ieq(((req as i64 + i) as *u8), "host:" as *u8, 5) == 1 { found = i + 5; break }
103 i = i + 1
104 }
105 if found < 0 { out[0] = (0 as u8); return 0 }
106 var k: i64 = found
107 while k < reqn { if req[k] == (32 as u8) { k = k + 1 } else { break } }
108 var j: i64 = 0
109 while k < reqn {
110 let c: u8 = req[k]
111 if c == (13 as u8) { break }
112 if c == (10 as u8) { break }
113 if c == (58 as u8) { break }
114 if c == (32 as u8) { break }
115 if j < cap - 1 { out[j] = hr_lower(c); j = j + 1 }
116 k = k + 1
117 }
118 out[j] = (0 as u8)
119 return j
120}
121
122// Find the doc root for host in the config (lines "<host> <root>"; '#' comment; '*' wildcard default).
123// Exact host wins over wildcard regardless of line order. Returns root length (0 = no match).
124func hr_lookup_root(cfg: *u8, cfgn: i64, host: *u8, hostn: i64, out: *u8, cap: i64) -> i64 {
125 var i: i64 = 0
126 var wild: i64 = 0 - 1
127 var wildlen: i64 = 0
128 while i < cfgn {
129 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 } } }
130 if i < cfgn {
131 if cfg[i] == (35 as u8) {
132 while i < cfgn { if cfg[i] == (10 as u8) { break } i = i + 1 }
133 i = i + 1
134 continue
135 }
136 }
137 let htok: i64 = i
138 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 }
139 let htoklen: i64 = i - htok
140 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 } } }
141 let rtok: i64 = i
142 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 }
143 let rtoklen: i64 = i - rtok
144 while i < cfgn { if cfg[i] == (10 as u8) { break } i = i + 1 }
145 i = i + 1
146 if htoklen == hostn {
147 if hr_eq(((cfg as i64 + htok) as *u8), host, hostn) == 1 {
148 if rtoklen > 0 { hr_copy(out, ((cfg as i64 + rtok) as *u8), rtoklen); out[rtoklen] = (0 as u8); return rtoklen }
149 }
150 }
151 if htoklen == 1 { if cfg[htok] == (42 as u8) { if rtoklen > 0 { wild = rtok; wildlen = rtoklen } } }
152 }
153 if wild >= 0 { if wildlen > 0 { hr_copy(out, ((cfg as i64 + wild) as *u8), wildlen); out[wildlen] = (0 as u8); return wildlen } }
154 return 0
155}
156
157// Reject path traversal / NUL / non-absolute. Conservative: any ".." anywhere fails.
158func hr_path_safe(path: *u8, pathn: i64) -> i64 {
159 if pathn == 0 { return 0 }
160 if path[0] != (47 as u8) { return 0 }
161 var i: i64 = 0
162 while i < pathn {
163 if path[i] == (0 as u8) { return 0 }
164 if path[i] == (46 as u8) { if i + 1 < pathn { if path[i + 1] == (46 as u8) { return 0 } } }
165 i = i + 1
166 }
167 return 1
168}
169
170// Join root + path into out; append index.html when the path ends in '/'.
171func hr_resolve(root: *u8, rootn: i64, path: *u8, pathn: i64, out: *u8, cap: i64) -> i64 {
172 var j: i64 = 0
173 var i: i64 = 0
174 while i < rootn { if j < cap - 1 { out[j] = root[i]; j = j + 1 } i = i + 1 }
175 if j > 0 { if out[j - 1] == (47 as u8) { j = j - 1 } }
176 i = 0
177 while i < pathn { if j < cap - 1 { out[j] = path[i]; j = j + 1 } i = i + 1 }
178 if pathn > 0 {
179 if path[pathn - 1] == (47 as u8) {
180 // path ends in '/': append index.html
181 let idx: *u8 = "index.html" as *u8
182 var k: i64 = 0
183 while idx[k] != (0 as u8) { if j < cap - 1 { out[j] = idx[k]; j = j + 1 } k = k + 1 }
184 } else {
185 // bare path: if the last segment has no '.', it is a directory ->
186 // append /index.html so a CLEAN URL (e.g. /econsim) serves the page
187 // instead of reading the dir as a 0-byte octet-stream download.
188 var seg_has_dot: i64 = 0
189 var s: i64 = pathn - 1
190 while s >= 0 {
191 if path[s] == (47 as u8) { break }
192 if path[s] == (46 as u8) { seg_has_dot = 1; break }
193 s = s - 1
194 }
195 if seg_has_dot == 0 {
196 let idx2: *u8 = "/index.html" as *u8
197 var k2: i64 = 0
198 while idx2[k2] != (0 as u8) { if j < cap - 1 { out[j] = idx2[k2]; j = j + 1 } k2 = k2 + 1 }
199 }
200 }
201 }
202 out[j] = (0 as u8)
203 return j
204}
205
206// MIME by extension.
207func hr_ctype(file: *u8, filen: i64) -> *u8 {
208 var dot: i64 = 0 - 1
209 var i: i64 = 0
210 while i < filen { if file[i] == (46 as u8) { dot = i } i = i + 1 }
211 if dot < 0 { return "application/octet-stream" as *u8 }
212 let ext: *u8 = (file as i64 + dot) as *u8
213 if hr_ieq(ext, ".html" as *u8, 5) == 1 { return "text/html; charset=utf-8" as *u8 }
214 if hr_ieq(ext, ".htm" as *u8, 4) == 1 { return "text/html; charset=utf-8" as *u8 }
215 if hr_ieq(ext, ".css" as *u8, 4) == 1 { return "text/css; charset=utf-8" as *u8 }
216 if hr_ieq(ext, ".json" as *u8, 5) == 1 { return "application/json" as *u8 }
217 // .xml (2026-07-30): sitemap.xml was being served as application/octet-stream because the map had no
218 // XML row -- the one file on the site whose entire purpose is to be parsed by a machine was the one
219 // told it was an opaque blob. RFC 7303 media type; sitemaps.org expects an XML content type.
220 if hr_ieq(ext, ".xml" as *u8, 4) == 1 { return "application/xml; charset=utf-8" as *u8 }
221 if hr_ieq(ext, ".js" as *u8, 3) == 1 { return "application/javascript" as *u8 }
222 if hr_ieq(ext, ".png" as *u8, 4) == 1 { return "image/png" as *u8 }
223 if hr_ieq(ext, ".jpg" as *u8, 4) == 1 { return "image/jpeg" as *u8 }
224 if hr_ieq(ext, ".jpeg" as *u8, 5) == 1 { return "image/jpeg" as *u8 }
225 if hr_ieq(ext, ".svg" as *u8, 4) == 1 { return "image/svg+xml" as *u8 }
226 if hr_ieq(ext, ".gif" as *u8, 4) == 1 { return "image/gif" as *u8 }
227 if hr_ieq(ext, ".txt" as *u8, 4) == 1 { return "text/plain; charset=utf-8" as *u8 }
228 if hr_ieq(ext, ".ico" as *u8, 4) == 1 { return "image/x-icon" as *u8 }
229 if hr_ieq(ext, ".pdf" as *u8, 4) == 1 { return "application/pdf" as *u8 }
230 if hr_ieq(ext, ".webmanifest" as *u8, 12) == 1 { return "application/manifest+json" as *u8 }
231 if hr_ieq(ext, ".wasm" as *u8, 5) == 1 { return "application/wasm" as *u8 }
232 if hr_ieq(ext, ".mp4" as *u8, 4) == 1 { return "video/mp4" as *u8 }
233 if hr_ieq(ext, ".webm" as *u8, 5) == 1 { return "video/webm" as *u8 }
234 if hr_ieq(ext, ".woff2" as *u8, 6) == 1 { return "font/woff2" as *u8 }
235 // interop artifacts (the office/coordination second-half): calendar subscribe + office docs
236 if hr_ieq(ext, ".ics" as *u8, 4) == 1 { return "text/calendar; charset=utf-8" as *u8 }
237 if hr_ieq(ext, ".docx" as *u8, 5) == 1 { return "application/vnd.openxmlformats-officedocument.wordprocessingml.document" as *u8 }
238 if hr_ieq(ext, ".xlsx" as *u8, 5) == 1 { return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" as *u8 }
239 if hr_ieq(ext, ".pptx" as *u8, 5) == 1 { return "application/vnd.openxmlformats-officedocument.presentationml.presentation" as *u8 }
240 if hr_ieq(ext, ".odt" as *u8, 4) == 1 { return "application/vnd.oasis.opendocument.text" as *u8 }
241 if hr_ieq(ext, ".eml" as *u8, 4) == 1 { return "message/rfc822" as *u8 }
242 return "application/octet-stream" as *u8
243}
244
245// Emit a full HTTP/1.1 response (status line + Content-Type + Content-Length + body) into out.
246func hr_emit(out: *u8, statusline: *u8, ctype: *u8, body: *u8, bodyn: i64) -> i64 {
247 var j: i64 = 0
248 var k: i64 = 0
249 while statusline[k] != (0 as u8) { out[j] = statusline[k]; j = j + 1; k = k + 1 }
250 let h1: *u8 = "\r\nContent-Type: " as *u8
251 k = 0; while h1[k] != (0 as u8) { out[j] = h1[k]; j = j + 1; k = k + 1 }
252 k = 0; while ctype[k] != (0 as u8) { out[j] = ctype[k]; j = j + 1; k = k + 1 }
253 let h2: *u8 = "\r\nContent-Length: " as *u8
254 k = 0; while h2[k] != (0 as u8) { out[j] = h2[k]; j = j + 1; k = k + 1 }
255 j = j + hr_putdec(((out as i64 + j) as *u8), bodyn)
256 let h3: *u8 = "\r\nConnection: close\r\nX-Served-By: nishi-host\r\n\r\n" as *u8
257 k = 0; while h3[k] != (0 as u8) { out[j] = h3[k]; j = j + 1; k = k + 1 }
258 var b: i64 = 0
259 while b < bodyn { out[j] = body[b]; j = j + 1; b = b + 1 }
260 return j
261}
262
263// Does the request's Host match a configured site? Lets the daemon route ONLY config hosts
264// through the file server and fall back to its legacy routing (e.g. the wiki) for the rest.
265func hr_known_host(cfg: *u8, cfgn: i64, req: *u8, reqn: i64) -> i64 {
266 if cfgn <= 0 { return 0 }
267 let host: *u8 = sys_mmap(256)
268 let root: *u8 = sys_mmap(HR_MAGIC_1024)
269 let hn: i64 = hr_req_host(req, reqn, host, 256)
270 if hr_lookup_root(cfg, cfgn, host, hn, root, HR_MAGIC_1024) > 0 { return 1 }
271 return 0
272}
273
274// Full pipeline: request bytes + config -> HTTP response bytes in out. Reads the file PER REQUEST
275// (hot content). outcap must hold headers + file. Returns response length.
276func hr_serve(cfg: *u8, cfgn: i64, req: *u8, reqn: i64, out: *u8, outcap: i64) -> i64 {
277 let host: *u8 = sys_mmap(256)
278 let path: *u8 = sys_mmap(HR_MAGIC_4096)
279 let root: *u8 = sys_mmap(HR_MAGIC_1024)
280 let file: *u8 = sys_mmap(HR_MAGIC_5120)
281 let hn: i64 = hr_req_host(req, reqn, host, 256)
282 let pn: i64 = hr_req_path(req, reqn, path, HR_MAGIC_4096)
283 let rn: i64 = hr_lookup_root(cfg, cfgn, host, hn, root, HR_MAGIC_1024)
284 if rn == 0 {
285 let b0: *u8 = "<!doctype html><meta charset=utf-8><title>404</title><h1>404 — unknown host</h1>" as *u8
286 return hr_emit(out, "HTTP/1.1 404 Not Found" as *u8, "text/html; charset=utf-8" as *u8, b0, hr_slen(b0))
287 }
288 if hr_path_safe(path, pn) == 0 {
289 let b1: *u8 = "<!doctype html><meta charset=utf-8><title>400</title><h1>400 — bad path</h1>" as *u8
290 return hr_emit(out, "HTTP/1.1 400 Bad Request" as *u8, "text/html; charset=utf-8" as *u8, b1, hr_slen(b1))
291 }
292 let fnlen: i64 = hr_resolve(root, rn, path, pn, file, HR_MAGIC_5120)
293 let lenbox: *i64 = (sys_mmap(8)) as *i64
294 lenbox[0] = 0
295 let data: *u8 = sys_read_file(file, lenbox)
296 if (data as i64) == 0 {
297 let b2: *u8 = "<!doctype html><meta charset=utf-8><title>404</title><h1>404 — not found</h1>" as *u8
298 return hr_emit(out, "HTTP/1.1 404 Not Found" as *u8, "text/html; charset=utf-8" as *u8, b2, hr_slen(b2))
299 }
300 let dn: i64 = lenbox[0]
301 let ct: *u8 = hr_ctype(file, fnlen)
302 return hr_emit(out, "HTTP/1.1 200 OK" as *u8, ct, data, dn)
303}
304
305// ===== v2: bounds-checked, keep-alive, fallthrough-aware serving =====
306// (ADDITIVE -- hr_serve/hr_emit above are unchanged; the deployed daemon's
307// behaviour is bit-identical until it opts into hr_serve2.)
308
309// Bounds-checked emit. Writes status + Content-Type + Content-Length +
310// keep-alive headers + body into out; returns total length, or -1 if it
311// would not fit in outcap (caller turns that into a 500). Keep-alive (not
312// close) so one TLS handshake serves the whole page's assets.
313// Asset cache window. HTML is NEVER cached (the slot rotates on a 60s window and the served-impression
314// counter rides the HTML, so a cached page would freeze both rotation and billing); an asset is a
315// different fact. Bounded rather than immutable because house creatives sit at STABLE urls -- anything
316// uploaded through nx_adnet_creative is content-addressed and could safely take far longer.
317// DECLARED ABOVE ITS READER ON PURPOSE: nx_parse refuses a forward const read rather than silently
318// letting it evaluate to 0, which would have emitted "max-age=0" and looked like it worked.
319const HR_ASSET_MAXAGE: i64 = 3600
320
321func hr_emit_b(out: *u8, outcap: i64, statusline: *u8, ctype: *u8, body: *u8, bodyn: i64) -> i64 {
322 // 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)
323 // ROOT FIX (2026-07-31, debt 1785513321). This was a BLANKET RESERVE: `bodyn + HR_MAGIC_1024 > outcap`,
324 // which for outcap=1024 is true for ANY non-empty body -- so hr_serve2's TOOBIG branch could never emit
325 // its own 500 and an oversize file became a SILENT CONNECTION DROP instead of an error. A guard that
326 // cannot be satisfied produces a bypass, not safety. nx_host_router2_test asserted the 500 and had been
327 // RED on that tooth, uncompiled and unnoticed.
328 // Now bounded PRECISELY: refuse an outcap too small to hold the header block at all, then check the
329 // ACTUAL accumulated header length against the body before writing a single body byte.
330 if outcap < HR_MAGIC_1024 { return 0 - 1 }
331 var j: i64 = 0
332 var k: i64 = 0
333 while statusline[k] != (0 as u8) { out[j] = statusline[k]; j = j + 1; k = k + 1 }
334 let h1: *u8 = "\r\nContent-Type: " as *u8
335 k = 0; while h1[k] != (0 as u8) { out[j] = h1[k]; j = j + 1; k = k + 1 }
336 k = 0; while ctype[k] != (0 as u8) { out[j] = ctype[k]; j = j + 1; k = k + 1 }
337 let h2: *u8 = "\r\nContent-Length: " as *u8
338 k = 0; while h2[k] != (0 as u8) { out[j] = h2[k]; j = j + 1; k = k + 1 }
339 j = j + hr_putdec(((out as i64 + j) as *u8), bodyn)
340 // F1120 CROSS-ORIGIN ISOLATION (2026-07-28; /video/capprobe MEASURED crossOriginIsolated=false as
341 // the R1 blocker): COOP same-origin + COEP require-corp are what let a page use SharedArrayBuffer =
342 // wasm THREADS = tile-parallel sovereign encode (the 60fps+/4K multiplier). BLAST RADIUS NEAR-ZERO
343 // BY CONSTRUCTION: the CSP here is already default-src 'self', so every subresource is same-origin
344 // and CORP: same-origin covers them. A HEADER CHANGE KILLED THIS PRODUCT FOR 5 DAYS (seq1073) --
345 // the video_canary tooth asserts these tokens too: a regression fails the same 300s pass it ships in.
346 // CACHEABILITY BY CONTENT TYPE (2026-07-31). This emitter hardcoded no-cache for EVERY response,
347 // images included, so a creative shipped on every served page was re-downloaded every single time.
348 // HTML MUST STAY no-cache: the slot rotates on a 60s window and the served-impression counter rides
349 // the HTML, so caching a page would freeze both rotation and billing. An asset is a different fact.
350 let hA: *u8 = "\r\nConnection: keep-alive\r\nKeep-Alive: timeout=65\r\nCache-Control: " as *u8
351 k = 0; while hA[k] != (0 as u8) { out[j] = hA[k]; j = j + 1; k = k + 1 }
352 var hr_is_html: i64 = 1
353 let hr_hm: *u8 = "text/html" as *u8
354 var hq: i64 = 0
355 while hq < 9 { if ctype[hq] != hr_hm[hq] { hr_is_html = 0; break } hq = hq + 1 }
356 if hr_is_html == 1 {
357 let hc1: *u8 = "no-cache" as *u8
358 k = 0; while hc1[k] != (0 as u8) { out[j] = hc1[k]; j = j + 1; k = k + 1 }
359 } else {
360 let hc2: *u8 = "public, max-age=" as *u8
361 k = 0; while hc2[k] != (0 as u8) { out[j] = hc2[k]; j = j + 1; k = k + 1 }
362 j = j + hr_putdec(((out as i64 + j) as *u8), HR_ASSET_MAXAGE)
363 }
364 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
365 k = 0; while h3[k] != (0 as u8) { out[j] = h3[k]; j = j + 1; k = k + 1 }
366 // MEASURED bound: j is the ACTUAL header length just emitted, not a guessed reserve. Checked before a
367 // single body byte is written, so the refusal is exact and the TOOBIG path can emit its own 500.
368 if j + bodyn > outcap { return 0 - 1 }
369 var b: i64 = 0
370 while b < bodyn { out[j] = body[b]; j = j + 1; b = b + 1 }
371 return j
372}
373
374// Serve verdicts for hr_serve2 (sealed enum).
375const HR_S2_MISS: i64 = 0 // host known but file absent -> caller falls through to legacy routing
376const HR_S2_OK: i64 = 1 // 200 written
377const HR_S2_BAD: i64 = 2 // 400 written (traversal/malformed)
378const HR_S2_TOOBIG: i64 = 3 // 500 written (file exceeds outcap)
379
380// v2 pipeline: like hr_serve but (a) bounds-checked emit, (b) keep-alive,
381// (c) MISS verdict instead of a baked 404 when the file is absent -- the
382// daemon decides per host whether to fall through to its legacy routing
383// (nishifamily wiki) or emit a real 404 (andelinwest). Response length is
384// written to *out_n; the verdict is the return value.
385func hr_serve2(cfg: *u8, cfgn: i64, req: *u8, reqn: i64, out: *u8, outcap: i64, out_n: *i64) -> i64 {
386 out_n[0] = 0
387 let host: *u8 = sys_mmap(256)
388 let path: *u8 = sys_mmap(HR_MAGIC_4096)
389 let root: *u8 = sys_mmap(HR_MAGIC_1024)
390 let file: *u8 = sys_mmap(HR_MAGIC_5120)
391 let hn: i64 = hr_req_host(req, reqn, host, 256)
392 let pn: i64 = hr_req_path(req, reqn, path, HR_MAGIC_4096)
393 let rn: i64 = hr_lookup_root(cfg, cfgn, host, hn, root, HR_MAGIC_1024)
394 if rn == 0 { return HR_S2_MISS }
395 if hr_path_safe(path, pn) == 0 {
396 let b1: *u8 = "<!doctype html><meta charset=utf-8><title>400</title><h1>400 — bad path</h1>" as *u8
397 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))
398 return HR_S2_BAD
399 }
400 let fnlen: i64 = hr_resolve(root, rn, path, pn, file, HR_MAGIC_5120)
401 let lenbox: *i64 = (sys_mmap(8)) as *i64
402 lenbox[0] = 0
403 let data: *u8 = sys_read_file(file, lenbox)
404 if (data as i64) == 0 { return HR_S2_MISS }
405 let dn: i64 = lenbox[0]
406 let ct: *u8 = hr_ctype(file, fnlen)
407 let w: i64 = hr_emit_b(out, outcap, "HTTP/1.1 200 OK" as *u8, ct, data, dn)
408 if w < 0 {
409 let b3: *u8 = "<!doctype html><meta charset=utf-8><title>500</title><h1>500 — file exceeds serve buffer</h1>" as *u8
410 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))
411 return HR_S2_TOOBIG
412 }
413 out_n[0] = w
414 return HR_S2_OK
415}
416
417// Real 404 for hosts that do NOT fall through (e.g. andelinwest.com).
418func hr_emit_404(out: *u8, outcap: i64) -> i64 {
419 let b2: *u8 = "<!doctype html><meta charset=utf-8><title>404</title><h1>404 — not found</h1>" as *u8
420 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))
421}
422
423// ===== v3: S-CLASS ROUTING -- clean URLs + canonical 301s, ONE resolver for every path =====
424// (ADDITIVE: hr_serve/hr_serve2 above are byte-identical and untouched.) This mirrors the gated pure
425// engine nx_route.nx (nx_route_gate 5/5) against the LIVE filesystem so routing is a property of the
426// HOST, not per-page redirect stubs:
427// * CLEAN extensionless URLs serve, for BOTH layouts: /foo -> foo/index.html (dir) OR foo.html (flat).
428// * ONE CANONICAL url via REAL 301s: /foo.html, /foo/, /foo/index.html, /index.html all collapse to
429// the clean canonical -- BUT only when that clean target actually resolves to a file, so a legacy
430// .html route (served by fallthrough, not a docroot file) is never 301'd into a 404.
431// * dir-index keeps PRECEDENCE over .html (so an existing /games hub is never shadowed by a stale flat).
432// * traversal -> 400 ; nothing resolves -> MISS (caller falls through to legacy, unchanged).
433const HR_S2_REDIR: i64 = 4 // 301 written to out (out_n set); caller must NOT fall through
434const HR_S2_STREAM: i64 = 5 // headers written to out; BODY handed via hr_stream_body/bodyn -- the
435 // caller sends both through its own chunked TLS primitive. ZERO-CEILING
436 // static serving (debt 1785879638): no file-size constant exists on this
437 // path; anything the host can mmap streams. Body ptr/len ride module
438 // statics (scalars, not arrays -- the BSS-array crash class): the daemon
439 // forks per connection, so the pair is per-process and consumed before
440 // the next request in that process.
441static hr_stream_p: i64 = 0
442static hr_stream_n: i64 = 0
443func hr_stream_body() -> i64 { return hr_stream_p }
444func hr_stream_bodyn() -> i64 { return hr_stream_n }
445
446// header half of hr_emit_b, byte-identical headers (incl cacheability-by-ctype + the security set);
447// Content-Length = bodyn, but NO body byte is written -- the stream caller sends the body itself.
448func hr_emit_head(out: *u8, outcap: i64, statusline: *u8, ctype: *u8, bodyn: i64) -> i64 {
449 if outcap < HR_MAGIC_1024 { return 0 - 1 }
450 var j: i64 = 0
451 var k: i64 = 0
452 while statusline[k] != (0 as u8) { out[j] = statusline[k]; j = j + 1; k = k + 1 }
453 let h1: *u8 = "\r\nContent-Type: " as *u8
454 k = 0; while h1[k] != (0 as u8) { out[j] = h1[k]; j = j + 1; k = k + 1 }
455 k = 0; while ctype[k] != (0 as u8) { out[j] = ctype[k]; j = j + 1; k = k + 1 }
456 let h2: *u8 = "\r\nContent-Length: " as *u8
457 k = 0; while h2[k] != (0 as u8) { out[j] = h2[k]; j = j + 1; k = k + 1 }
458 j = j + hr_putdec(((out as i64 + j) as *u8), bodyn)
459 let hA: *u8 = "\r\nConnection: keep-alive\r\nKeep-Alive: timeout=65\r\nCache-Control: " as *u8
460 k = 0; while hA[k] != (0 as u8) { out[j] = hA[k]; j = j + 1; k = k + 1 }
461 var hh: i64 = 1
462 let hm: *u8 = "text/html" as *u8
463 var hq: i64 = 0
464 while hq < 9 { if ctype[hq] != hm[hq] { hh = 0; break } hq = hq + 1 }
465 if hh == 1 {
466 let hc1: *u8 = "no-cache" as *u8
467 k = 0; while hc1[k] != (0 as u8) { out[j] = hc1[k]; j = j + 1; k = k + 1 }
468 } else {
469 let hc2: *u8 = "public, max-age=" as *u8
470 k = 0; while hc2[k] != (0 as u8) { out[j] = hc2[k]; j = j + 1; k = k + 1 }
471 j = j + hr_putdec(((out as i64 + j) as *u8), HR_ASSET_MAXAGE)
472 }
473 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
474 k = 0; while h3[k] != (0 as u8) { out[j] = h3[k]; j = j + 1; k = k + 1 }
475 if j > outcap { return 0 - 1 }
476 return j
477}
478
479func hr_ends2(s: *u8, n: i64, suf: *u8, sufn: i64) -> i64 {
480 if sufn > n { return 0 }
481 var i: i64 = 0
482 while i < sufn { if s[n - sufn + i] != suf[i] { return 0 } i = i + 1 }
483 return 1
484}
485
486// Canonical clean form of path -> loc (NUL-terminated). Returns locn (>0 => a redirect is warranted),
487// or 0 when the path is already canonical. Order: /index.html before .html (the former is a suffix of it).
488func hr_canon(path: *u8, pn: i64, loc: *u8) -> i64 {
489 if pn <= 1 { return 0 }
490 if hr_ends2(path, pn, "/index.html" as *u8, 11) == 1 {
491 let m1: i64 = pn - 11
492 if m1 <= 0 { loc[0] = 47 as u8; loc[1] = 0 as u8; return 1 }
493 var i1: i64 = 0; while i1 < m1 { loc[i1] = path[i1]; i1 = i1 + 1 } loc[m1] = 0 as u8; return m1
494 }
495 if hr_ends2(path, pn, ".html" as *u8, 5) == 1 {
496 let m2: i64 = pn - 5
497 if m2 <= 0 { return 0 }
498 var i2: i64 = 0; while i2 < m2 { loc[i2] = path[i2]; i2 = i2 + 1 } loc[m2] = 0 as u8; return m2
499 }
500 if path[pn - 1] == (47 as u8) {
501 let m3: i64 = pn - 1
502 var i3: i64 = 0; while i3 < m3 { loc[i3] = path[i3]; i3 = i3 + 1 } loc[m3] = 0 as u8; return m3
503 }
504 return 0
505}
506
507// Join root (trailing '/' trimmed) + path + suffix into file; return length.
508func hr_join(root: *u8, rn: i64, path: *u8, pn: i64, suffix: *u8, file: *u8, cap: i64) -> i64 {
509 var j: i64 = 0; var i: i64 = 0
510 while i < rn { if j < cap - 1 { file[j] = root[i]; j = j + 1 } i = i + 1 }
511 if j > 0 { if file[j - 1] == (47 as u8) { j = j - 1 } }
512 i = 0
513 while i < pn { if j < cap - 1 { file[j] = path[i]; j = j + 1 } i = i + 1 }
514 if (suffix as i64) != 0 {
515 i = 0
516 while suffix[i] != (0 as u8) { if j < cap - 1 { file[j] = suffix[i]; j = j + 1 } i = i + 1 }
517 }
518 file[j] = 0 as u8
519 return j
520}
521
522// Resolve path -> the first existing candidate file (read PER REQUEST = hot content). Candidate order:
523// "/" -> index.html ; dotted last segment (asset) -> exact ; clean URL -> dir-index THEN .html.
524// Returns data ptr (0 = nothing resolved); sets *fnlen (file-path length, for MIME) + *dn (byte count).
525func hr_resolve_read(root: *u8, rn: i64, path: *u8, pn: i64, file: *u8, cap: i64, fnlen: *i64, dn: *i64) -> *u8 {
526 let lenbox: *i64 = (sys_mmap(8)) as *i64; lenbox[0] = 0
527 var data: *u8 = 0 as *u8
528 if pn == 1 {
529 fnlen[0] = hr_join(root, rn, path, pn, "index.html" as *u8, file, cap)
530 data = sys_read_file(file, lenbox)
531 } else {
532 var seg_dot: i64 = 0; var s: i64 = pn - 1
533 while s >= 0 { if path[s] == (47 as u8) { break } if path[s] == (46 as u8) { seg_dot = 1; break } s = s - 1 }
534 if seg_dot == 1 {
535 fnlen[0] = hr_join(root, rn, path, pn, 0 as *u8, file, cap)
536 data = sys_read_file(file, lenbox)
537 } else {
538 fnlen[0] = hr_join(root, rn, path, pn, "/index.html" as *u8, file, cap)
539 data = sys_read_file(file, lenbox)
540 if (data as i64) == 0 {
541 fnlen[0] = hr_join(root, rn, path, pn, ".html" as *u8, file, cap)
542 data = sys_read_file(file, lenbox)
543 }
544 }
545 }
546 dn[0] = lenbox[0]
547 return data
548}
549
550// Emit a 301 with Location (empty body, keep-alive). -1 if it would not fit.
551func hr_emit_redirect(out: *u8, outcap: i64, loc: *u8, locn: i64) -> i64 {
552 if locn + 256 > outcap { return 0 - 1 }
553 var j: i64 = 0; var k: i64 = 0
554 let s1: *u8 = "HTTP/1.1 301 Moved Permanently\r\nLocation: " as *u8
555 k = 0; while s1[k] != (0 as u8) { out[j] = s1[k]; j = j + 1; k = k + 1 }
556 k = 0; while k < locn { out[j] = loc[k]; j = j + 1; k = k + 1 }
557 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
558 k = 0; while s2[k] != (0 as u8) { out[j] = s2[k]; j = j + 1; k = k + 1 }
559 return j
560}
561
562// ===== v3+slot helpers: universal ad-slot injection (ADDITIVE -- hr_serve3 below is untouched) =====
563// hr_serve3_slot behaves byte-identically to hr_serve3 when slot_n==0 (gated). With slot bytes: a
564// text/html body containing </body> and NOT carrying the nx-ad-optout marker gets the slot inserted
565// before the LAST </body>; Content-Length stays correct by construction (merge happens before emit).
566// injected[0]=1 ONLY when the slot actually landed (the caller logs a served impression off it).
567
568func hr_ct_is_html(ct: *u8) -> i64 {
569 return hr_eq(ct, "text/html" as *u8, 9)
570}
571
572// index of the LAST "</body>" (case-insensitive) in body, or -1.
573func hr_find_close_body(body: *u8, bn: i64) -> i64 {
574 if bn < 7 { return 0 - 1 }
575 var i: i64 = bn - 7
576 while i >= 0 {
577 if body[i] == (60 as u8) {
578 if hr_ieq(((body as i64 + i) as *u8), "</body>" as *u8, 7) == 1 { return i }
579 }
580 i = i - 1
581 }
582 return 0 - 1
583}
584
585// page-level opt-out marker scan (any page may carry nx-ad-optout to refuse the slot).
586func hr_has_optout(body: *u8, bn: i64) -> i64 {
587 var i: i64 = 0
588 while i + 12 <= bn {
589 if body[i] == (110 as u8) {
590 if hr_eq(((body as i64 + i) as *u8), "nx-ad-optout" as *u8, 12) == 1 { return 1 }
591 }
592 i = i + 1
593 }
594 return 0
595}
596
597
598// ---- SLOT ANCHOR (2026-07-31): let a PAGE choose where its ad goes -------------------------------
599// WHY: the slot was always injected before the closing body tag -- the absolute bottom of the document.
600// On a long reference page essentially nobody scrolls there, and the MRC rule needs 50% of the creative
601// in view for one CONTINUOUS second. So viewable impressions were structurally near-zero: we measured
602// honestly and still sold a position nobody sees. The honest number does not fix placement, it EXPOSES
603// it -- and this is the mechanism that lets it be fixed.
604//
605// A page opts in by placing the marker where it wants the ad. NO MARKER = the old behaviour, byte for
606// byte, so this cannot change a single existing page. Placement stays a PRODUCT decision made per page,
607// not an engineering default imposed on every site at once.
608const HR_SLOT_MARK: *u8 = "<!--nx-ad-slot-->" as *u8
609
610func hr_find_slot_anchor(d: *u8, n: i64) -> i64 {
611 var i: i64 = 0
612 while i + 17 <= n {
613 var k: i64 = 0
614 var m: i64 = 1
615 while k < 17 { if d[i + k] != HR_SLOT_MARK[k] { m = 0; break } k = k + 1 }
616 if m == 1 { return i }
617 i = i + 1
618 }
619 return hr_find_close_body(d, n)
620}
621
622func 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 {
623 injected[0] = 0
624 out_n[0] = 0
625 let host: *u8 = sys_mmap(256)
626 let path: *u8 = sys_mmap(HR_MAGIC_4096)
627 let root: *u8 = sys_mmap(HR_MAGIC_1024)
628 let file: *u8 = sys_mmap(HR_MAGIC_5120)
629 let hn: i64 = hr_req_host(req, reqn, host, 256)
630 let pn: i64 = hr_req_path(req, reqn, path, HR_MAGIC_4096)
631 let rn: i64 = hr_lookup_root(cfg, cfgn, host, hn, root, HR_MAGIC_1024)
632 if rn == 0 { return HR_S2_MISS }
633 if hr_path_safe(path, pn) == 0 {
634 let b1: *u8 = "<!doctype html><meta charset=utf-8><title>400</title><h1>400 — bad path</h1>" as *u8
635 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))
636 return HR_S2_BAD
637 }
638 let fnlen: *i64 = (sys_mmap(8)) as *i64
639 let dn: *i64 = (sys_mmap(8)) as *i64
640 let loc: *u8 = sys_mmap(HR_MAGIC_4096)
641 let locn: i64 = hr_canon(path, pn, loc)
642 if locn > 0 {
643 let cfile: *u8 = sys_mmap(HR_MAGIC_5120)
644 let cdata: *u8 = hr_resolve_read(root, rn, loc, locn, cfile, HR_MAGIC_5120, fnlen, dn)
645 if (cdata as i64) != 0 {
646 // seq365: CARRY the ?query on the canonical 301 (resolve above used the PATH-only loc;
647 // append after resolve so filesystem lookup never sees query bytes). No-query requests
648 // emit byte-identical redirects to before.
649 let qbuf: *u8 = sys_mmap(HR_MAGIC_2048)
650 let qn: i64 = hr_req_query(req, reqn, qbuf, HR_MAGIC_2048)
651 var locq: i64 = locn
652 if qn > 0 { if locn + qn < HR_MAGIC_4095 {
653 var qi: i64 = 0
654 while qi < qn { loc[locn + qi] = qbuf[qi]; qi = qi + 1 }
655 locq = locn + qn
656 loc[locq] = 0 as u8
657 } }
658 out_n[0] = hr_emit_redirect(out, outcap, loc, locq)
659 if out_n[0] < 0 { out_n[0] = 0; return HR_S2_MISS }
660 return HR_S2_REDIR
661 }
662 }
663 let data: *u8 = hr_resolve_read(root, rn, path, pn, file, HR_MAGIC_5120, fnlen, dn)
664 if (data as i64) == 0 { return HR_S2_MISS }
665 let ct: *u8 = hr_ctype(file, fnlen[0])
666 var body: *u8 = data
667 var bodyn: i64 = dn[0]
668 if slot_n > 0 {
669 if hr_ct_is_html(ct) == 1 {
670 let cb: i64 = hr_find_slot_anchor(data, bodyn)
671 if cb >= 0 {
672 let oo: i64 = hr_has_optout(data, bodyn)
673 if oo == 0 {
674 let mcap: i64 = bodyn + slot_n + 16
675 let merged: *u8 = sys_mmap(mcap)
676 var w2: i64 = 0
677 var a: i64 = 0
678 while a < cb { merged[w2] = data[a]; w2 = w2 + 1; a = a + 1 }
679 var b2: i64 = 0
680 while b2 < slot_n { merged[w2] = slot[b2]; w2 = w2 + 1; b2 = b2 + 1 }
681 while a < bodyn { merged[w2] = data[a]; w2 = w2 + 1; a = a + 1 }
682 body = merged
683 bodyn = w2
684 injected[0] = 1
685 }
686 }
687 }
688 }
689 let w: i64 = hr_emit_b(out, outcap, "HTTP/1.1 200 OK" as *u8, ct, body, bodyn)
690 if w < 0 {
691 // ZERO-CEILING STREAM (debt 1785879638; operator: no self-imposed limits, no magic numbers):
692 // the body is ALREADY in memory -- emit headers only (they always fit any sane outcap) and
693 // hand the body pointer to the daemon, which sends both through the same chunked TLS send.
694 // No file-size constant exists on this path. 500 remains ONLY for an outcap below the header floor.
695 let hw: i64 = hr_emit_head(out, outcap, "HTTP/1.1 200 OK" as *u8, ct, bodyn)
696 if hw > 0 {
697 hr_stream_p = body as i64
698 hr_stream_n = bodyn
699 out_n[0] = hw
700 return HR_S2_STREAM
701 }
702 let b3: *u8 = "<!doctype html><meta charset=utf-8><title>500</title><h1>500 — file exceeds serve buffer</h1>" as *u8
703 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))
704 return HR_S2_TOOBIG
705 }
706 out_n[0] = w
707 return HR_S2_OK
708}
709
710// v3 pipeline: canonical 301 (safe) + clean multi-candidate resolve. Verdicts: OK/MISS/BAD/TOOBIG/REDIR.
711func hr_serve3(cfg: *u8, cfgn: i64, req: *u8, reqn: i64, out: *u8, outcap: i64, out_n: *i64) -> i64 {
712 out_n[0] = 0
713 let host: *u8 = sys_mmap(256)
714 let path: *u8 = sys_mmap(HR_MAGIC_4096)
715 let root: *u8 = sys_mmap(HR_MAGIC_1024)
716 let file: *u8 = sys_mmap(HR_MAGIC_5120)
717 let hn: i64 = hr_req_host(req, reqn, host, 256)
718 let pn: i64 = hr_req_path(req, reqn, path, HR_MAGIC_4096)
719 let rn: i64 = hr_lookup_root(cfg, cfgn, host, hn, root, HR_MAGIC_1024)
720 if rn == 0 { return HR_S2_MISS }
721 if hr_path_safe(path, pn) == 0 {
722 let b1: *u8 = "<!doctype html><meta charset=utf-8><title>400</title><h1>400 — bad path</h1>" as *u8
723 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))
724 return HR_S2_BAD
725 }
726 let fnlen: *i64 = (sys_mmap(8)) as *i64
727 let dn: *i64 = (sys_mmap(8)) as *i64
728 // canonical 301 -- only when the clean target actually resolves (never 301 to a 404)
729 let loc: *u8 = sys_mmap(HR_MAGIC_4096)
730 let locn: i64 = hr_canon(path, pn, loc)
731 if locn > 0 {
732 let cfile: *u8 = sys_mmap(HR_MAGIC_5120)
733 let cdata: *u8 = hr_resolve_read(root, rn, loc, locn, cfile, HR_MAGIC_5120, fnlen, dn)
734 if (cdata as i64) != 0 {
735 // seq365: CARRY the ?query on the canonical 301 (resolve above used the PATH-only loc;
736 // append after resolve so filesystem lookup never sees query bytes). No-query requests
737 // emit byte-identical redirects to before.
738 let qbuf: *u8 = sys_mmap(HR_MAGIC_2048)
739 let qn: i64 = hr_req_query(req, reqn, qbuf, HR_MAGIC_2048)
740 var locq: i64 = locn
741 if qn > 0 { if locn + qn < HR_MAGIC_4095 {
742 var qi: i64 = 0
743 while qi < qn { loc[locn + qi] = qbuf[qi]; qi = qi + 1 }
744 locq = locn + qn
745 loc[locq] = 0 as u8
746 } }
747 out_n[0] = hr_emit_redirect(out, outcap, loc, locq)
748 if out_n[0] < 0 { out_n[0] = 0; return HR_S2_MISS }
749 return HR_S2_REDIR
750 }
751 }
752 // resolve + serve the (canonical) path
753 let data: *u8 = hr_resolve_read(root, rn, path, pn, file, HR_MAGIC_5120, fnlen, dn)
754 if (data as i64) == 0 { return HR_S2_MISS }
755 let ct: *u8 = hr_ctype(file, fnlen[0])
756 let w: i64 = hr_emit_b(out, outcap, "HTTP/1.1 200 OK" as *u8, ct, data, dn[0])
757 if w < 0 {
758 let b3: *u8 = "<!doctype html><meta charset=utf-8><title>500</title><h1>500 — file exceeds serve buffer</h1>" as *u8
759 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))
760 return HR_S2_TOOBIG
761 }
762 out_n[0] = w
763 return HR_S2_OK
764}