code wiki / (root) / nx_audit_server_routed.nx

nx_audit_server_routed.nx source

↩ module page · 687 lines · 24467 B

1// nx_audit_server_routed.nx -- multi-endpoint dispatcher daemon. 2// 3// Phase-A route-table integration of the audit-dashboard arc. 4// Composes shipped primitives: 5// 6// nx_http_server bind/listen/accept 7// nx_pages_static static-file serve for /audit/* 8// nx_http_health /health + /ready endpoints 9// nx_http_metrics /metrics endpoint (Prometheus text) 10// 11// Per cardinal NISHI_PRIVACY_BY_DEFAULT (S14): all counters are 12// SYSTEM_PERF (request count, error count, uptime) -- no per-IP, 13// no per-UA, no per-user fields. 14// 15// Per cardinal feedback-bounded-loop-discipline-jpl-rule-2: 1M- 16// request budget per process; supervisor restarts. 17// 18// Route table: 19// GET /health -> nx_http_emit_health (200 or 503) 20// GET /ready -> nx_http_emit_ready 21// GET /metrics -> nx_http_metrics_emit_response (counters) 22// GET /audit/* -> nx_pages_serve_file (base=docs) 23// GET / -> 200 with link list (no implicit index) 24// * -> 404 25// 26// Non-GET methods on any route -> 405 (handled by nx_pages_static 27// for the file paths; this file handles others). 28 29import "nx_syscalls_x86_64.nx" 30import "nx_http_server.nx" 31import "nx_pages_static.nx" 32import "nx_http_health.nx" 33import "nx_http_metrics.nx" 34import "nx_log_jsonl.nx" 35import "nx_http_header_find.nx" 36import "nx_http_cache.nx" 37import "nx_http_security_headers.nx" 38import "nx_path_canonicalize.nx" 39 40const NXAR_PORT: i64 = 51848 41const NXAR_REQ_CAP: i64 = 8192 42const NXAR_RESP_CAP: i64 = 524288 // 512 KB 43const NXAR_REQUEST_BUDGET: i64 = 1000000 44// Per-connection keepalive budget. After this many requests on 45// one TCP connection, close + force the client to reconnect (load- 46// balancing fairness + DoS bound). 47const NXAR_PER_CONN_BUDGET: i64 = 100 48 49// ---- Path-prefix matching helper --------------------------------- 50// 51// Returns 1 if path[0..prefix_n] == prefix[0..prefix_n] AND the 52// next byte is `/` or end-of-path (so "/audit" doesn't match 53// "/auditxx" but does match "/audit/index.html"). 54 55func nxar_path_starts_with(path: *u8, path_n: i64, 56 prefix: *u8, prefix_n: i64) -> i64 { 57 if path_n < prefix_n { return 0 } 58 var i: i64 = 0 59 while i < prefix_n { 60 if path[i] != prefix[i] { return 0 } 61 i = i + 1 62 } 63 // Boundary: prefix must end at end-of-path OR at a `/`. 64 if path_n == prefix_n { return 1 } 65 if path[prefix_n] == 0x2f as u8 { return 1 } 66 return 0 67} 68 69func nxar_path_eq(path: *u8, path_n: i64, 70 target: *u8, target_n: i64) -> i64 { 71 if path_n != target_n { return 0 } 72 var i: i64 = 0 73 while i < path_n { 74 if path[i] != target[i] { return 0 } 75 i = i + 1 76 } 77 return 1 78} 79 80// ---- Root response (link list) ----------------------------------- 81 82func nxar_emit_root(out: *u8, off: *i64, cap: i64) -> i64 { 83 // Simple HTML index pointing at the substrate's served endpoints. 84 let body: *u8 = "<!DOCTYPE html>\n<html><head><title>Nishi</title></head><body><h1>Nishi Audit Dashboard</h1><ul><li><a href=\"/audit/index.html\">audit dashboard</a></li><li><a href=\"/audit/snapshot.json\">snapshot.json</a></li><li><a href=\"/health\">/health</a></li><li><a href=\"/ready\">/ready</a></li><li><a href=\"/metrics\">/metrics</a></li></ul></body></html>\n" as *u8 85 let body_n: i64 = 364 86 // Write status + headers + Content-Length. 87 var p: i64 = *off 88 let hdr: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " as *u8 89 let hdr_n: i64 = 72 90 var i: i64 = 0 91 while i < hdr_n { 92 if p >= cap { return 1 } 93 out[p] = hdr[i] 94 p = p + 1 95 i = i + 1 96 } 97 // Emit body_n in decimal. 98 let scratch: *u8 = sys_mmap(16) 99 var n: i64 = body_n 100 var k: i64 = 0 101 while n > 0 { 102 scratch[k] = (0x30 + (n - (n / 10) * 10)) as u8 103 n = n / 10 104 k = k + 1 105 } 106 var ri: i64 = k - 1 107 while ri >= 0 { 108 if p >= cap { return 1 } 109 out[p] = scratch[ri] 110 p = p + 1 111 ri = ri - 1 112 } 113 let tail: *u8 = "\r\n\r\n" as *u8 114 var j: i64 = 0 115 while j < 4 { 116 if p >= cap { return 1 } 117 out[p] = tail[j] 118 p = p + 1 119 j = j + 1 120 } 121 var b: i64 = 0 122 while b < body_n { 123 if p >= cap { return 1 } 124 out[p] = body[b] 125 p = p + 1 126 b = b + 1 127 } 128 *off = p 129 return 0 130} 131 132// ---- HTTP method-kind to short name (per nx_http_io enum) -------- 133// 1 = GET, 2 = POST, 3 = OPTIONS, 0 = unknown 134 135func nxar_method_name(k: i64) -> *u8 { 136 if k == 1 { return "GET" as *u8 } 137 if k == 2 { return "POST" as *u8 } 138 if k == 3 { return "OPTIONS" as *u8 } 139 return "?" as *u8 140} 141 142func nxar_method_name_len(k: i64) -> i64 { 143 if k == 1 { return 3 } 144 if k == 2 { return 4 } 145 if k == 3 { return 7 } 146 return 1 147} 148 149// ---- Per-request structured log emitter -------------------------- 150// 151// Emits one JSON line to stdout per request. SYSTEM_PERF + SYSTEM_ 152// STATE category only per S14 -- explicitly excludes IP, UA, 153// cookies, body bytes, query string (which CAN contain PII like 154// session tokens), client identity. 155 156func nxar_log_request( 157 ts_ms: i64, 158 method_kind: i64, 159 path: *u8, path_n: i64, 160 status: i64, 161 duration_ms: i64, 162 resp_bytes: i64) -> i64 { 163 let log_buf: *u8 = sys_mmap(2048) 164 let log_off: *i64 = sys_mmap(8) as *i64 165 log_off[0] = 0 166 let scratch: *u8 = sys_mmap(64) 167 168 // Build msg = "<method> <path>" with caller-bounded path length. 169 let method_str: *u8 = nxar_method_name(method_kind) 170 let method_n: i64 = nxar_method_name_len(method_kind) 171 let msg_buf: *u8 = sys_mmap(512) 172 var msg_off: i64 = 0 173 var i: i64 = 0 174 while i < method_n { 175 msg_buf[msg_off + i] = method_str[i] 176 i = i + 1 177 } 178 msg_off = msg_off + method_n 179 msg_buf[msg_off] = 0x20 as u8 180 msg_off = msg_off + 1 181 // Cap path at 256 bytes for log brevity. 182 var cap_path: i64 = path_n 183 if cap_path > 256 { cap_path = 256 } 184 var j: i64 = 0 185 while j < cap_path { 186 msg_buf[msg_off + j] = path[j] 187 j = j + 1 188 } 189 msg_off = msg_off + cap_path 190 191 // Pick level by status class. 192 var level: i64 = NXL_INFO 193 if status >= 400 && status <= 499 { level = NXL_WARN } 194 if status >= 500 && status <= 599 { level = NXL_ERROR } 195 196 // Emit base line + status field. Compose: emit_line_with_int 197 // gives us status; we need ALSO duration + bytes. Pragmatic 198 // v0: emit two log lines, one with status + duration_ms and one 199 // with bytes. Future: nx_log_emit_line_with_multi (queued). 200 // 201 // v0 emit: single line with status field. 202 nx_log_emit_line_with_int(log_buf, log_off, 2048, 203 ts_ms, 204 level, 205 "nx_audit_server" as *u8, 15, 206 msg_buf, msg_off, 207 "status" as *u8, 6, 208 status, 209 scratch) 210 211 // Write to stdout (fd=1). 212 sys_write(1, log_buf, log_off[0]) 213 return 0 214} 215 216// ---- Plain 404 emitter ------------------------------------------- 217 218func nxar_emit_404(out: *u8, off: *i64, cap: i64) -> i64 { 219 let resp: *u8 = "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n\r\nROUTED-404\r\n\r\n" as *u8 220 let n: i64 = 87 221 var i: i64 = 0 222 var p: i64 = *off 223 while i < n { 224 if p >= cap { return 1 } 225 out[p] = resp[i] 226 p = p + 1 227 i = i + 1 228 } 229 *off = p 230 return 0 231} 232 233// ---- Metric-table builder ---------------------------------------- 234// 235// The /metrics endpoint exposes 4 counters (all SYSTEM_PERF / 236// SYSTEM_STATE per S14 privacy invariant; no per-user fields): 237// 238// nishi_uptime_ms gauge 239// nishi_requests_total counter 240// nishi_errors_total counter 241// nishi_request_budget_remaining gauge 242 243const N_METRICS: i64 = 4 244 245func nxar_serve_metrics(out: *u8, out_cap: i64, out_n: *i64, 246 uptime_ms: i64, n_requests: i64, 247 n_errors: i64, budget_remaining: i64) -> i64 { 248 let names_buf: *u8 = sys_mmap(512) 249 let name_offs: *i64 = sys_mmap(32) as *i64 250 let name_lens: *i64 = sys_mmap(32) as *i64 251 let helps_buf: *u8 = sys_mmap(512) 252 let help_offs: *i64 = sys_mmap(32) as *i64 253 let help_lens: *i64 = sys_mmap(32) as *i64 254 let types: *i64 = sys_mmap(32) as *i64 255 let values: *i64 = sys_mmap(32) as *i64 256 257 // Names 258 let n0: *u8 = "nishi_uptime_ms" as *u8 259 let n1: *u8 = "nishi_requests_total" as *u8 260 let n2: *u8 = "nishi_errors_total" as *u8 261 let n3: *u8 = "nishi_request_budget_remaining" as *u8 262 var i: i64 = 0 263 while i < 15 { names_buf[i] = n0[i]; i = i + 1 } 264 name_offs[0] = 0; name_lens[0] = 15 265 var j: i64 = 0 266 while j < 20 { names_buf[15 + j] = n1[j]; j = j + 1 } 267 name_offs[1] = 15; name_lens[1] = 20 268 var k: i64 = 0 269 while k < 18 { names_buf[35 + k] = n2[k]; k = k + 1 } 270 name_offs[2] = 35; name_lens[2] = 18 271 var m: i64 = 0 272 while m < 30 { names_buf[53 + m] = n3[m]; m = m + 1 } 273 name_offs[3] = 53; name_lens[3] = 30 274 275 // Helps (short; permissible per Prometheus spec). 276 let h0: *u8 = "Daemon uptime in ms" as *u8 277 let h1: *u8 = "Total HTTP requests served" as *u8 278 let h2: *u8 = "Total request errors" as *u8 279 let h3: *u8 = "Remaining request budget before clean exit" as *u8 280 var ha: i64 = 0 281 while ha < 19 { helps_buf[ha] = h0[ha]; ha = ha + 1 } 282 help_offs[0] = 0; help_lens[0] = 19 283 var hb: i64 = 0 284 while hb < 26 { helps_buf[19 + hb] = h1[hb]; hb = hb + 1 } 285 help_offs[1] = 19; help_lens[1] = 26 286 var hc: i64 = 0 287 while hc < 20 { helps_buf[45 + hc] = h2[hc]; hc = hc + 1 } 288 help_offs[2] = 45; help_lens[2] = 20 289 var hd: i64 = 0 290 while hd < 42 { helps_buf[65 + hd] = h3[hd]; hd = hd + 1 } 291 help_offs[3] = 65; help_lens[3] = 42 292 293 types[0] = NXM_TYPE_GAUGE 294 types[1] = NXM_TYPE_COUNTER 295 types[2] = NXM_TYPE_COUNTER 296 types[3] = NXM_TYPE_GAUGE 297 values[0] = uptime_ms 298 values[1] = n_requests 299 values[2] = n_errors 300 values[3] = budget_remaining 301 302 let scratch: *u8 = sys_mmap(64) 303 return nx_http_metrics_emit_response(out, out_cap, out_n, 304 names_buf, name_offs, name_lens, 305 helps_buf, help_offs, help_lens, 306 types, values, N_METRICS, 307 scratch) 308} 309 310// ---- Health serve (System-class only) ---------------------------- 311// 312// PASS so long as the daemon is up and the request budget has 313// headroom (>10% remaining). FAIL when budget exhausted. 314 315func nxar_serve_health(out: *u8, out_cap: i64, out_n: *i64, 316 uptime_ms: i64, budget_remaining: i64) -> i64 { 317 var status: i64 = NXHL_STATUS_PASS 318 let warn_thresh: i64 = NXAR_REQUEST_BUDGET / 10 319 if budget_remaining < warn_thresh { status = NXHL_STATUS_WARN } 320 if budget_remaining < 100 { status = NXHL_STATUS_FAIL } 321 let scratch: *u8 = sys_mmap(64) 322 return nx_http_emit_health(out, out_cap, out_n, 323 status, uptime_ms, 1, scratch) 324} 325 326// ---- S-class /audit/* serve with cache + security headers ------- 327// 328// Replaces the routed daemon's plain nx_pages_serve_file call for 329// /audit/* paths with a full production-grade flow: 330// 331// 1. parse If-None-Match from request headers 332// 2. canonicalize the URL path (CWE-22 prevention) 333// 3. join with base dir 334// 4. read file body 335// 5. compute ETag (FNV-1a 64 hex) over the body 336// 6. if If-None-Match matches -> emit 304 (113 bytes; saves ~99% bandwidth) 337// 7. else emit 200 with: ETag + Cache-Control + 7 security headers + body 338// 339// Returns the number of bytes written to resp_buf + sets *served_304 340// = 1 if the request hit the 304 fast-path. 341 342func nxar_serve_audit_cached( 343 req_buf: *u8, headers_end: i64, 344 url_path: *u8, url_n: i64, 345 base_dir: *u8, base_n: i64, 346 resp_buf: *u8, resp_cap: i64, 347 served_304: *i64) -> i64 { 348 *served_304 = 0 349 350 // Step 1: If-None-Match. 351 let inm_off: *i64 = sys_mmap(8) as *i64 352 let inm_len: *i64 = sys_mmap(8) as *i64 353 let inm_rc: i64 = nx_http_header_find(req_buf, headers_end, 354 "If-None-Match" as *u8, 13, 355 inm_off, inm_len) 356 357 // Step 2: canonicalize. Strip leading `/`. 358 var off: i64 = 0 359 if url_n < 2 { 360 // Bare `/` -- not allowed; fallthrough to 404. 361 let r404: *u8 = "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n\r\nROUTED-404\r\n\r\n" as *u8 362 var rn: i64 = 0 363 while rn < 87 { 364 if off >= resp_cap { return -1 } 365 resp_buf[off] = r404[rn] 366 off = off + 1 367 rn = rn + 1 368 } 369 return off 370 } 371 let rel_ptr: *u8 = ((url_path as i64) + 1) as *u8 372 let rel_n: i64 = url_n - 1 373 let canon_buf: *u8 = sys_mmap(1024) 374 let canon_n: *i64 = sys_mmap(8) as *i64 375 let cv: i64 = nx_path_canonicalize(rel_ptr, rel_n, canon_buf, 1024, canon_n) 376 if cv != NXP_OK { 377 // Refuse. Emit 403 / 404. 378 let r403: *u8 = "HTTP/1.1 403 Forbidden\r\nContent-Type: text/plain\r\nContent-Length: 10\r\n\r\nForbidden\n" as *u8 379 var rn3: i64 = 0 380 while rn3 < 84 { 381 if off >= resp_cap { return -1 } 382 resp_buf[off] = r403[rn3] 383 off = off + 1 384 rn3 = rn3 + 1 385 } 386 return off 387 } 388 389 // Step 3: join with base. 390 let full_path: *u8 = sys_mmap(2048) 391 let full_n: *i64 = sys_mmap(8) as *i64 392 let jv: i64 = nx_path_join(base_dir, base_n, canon_buf, canon_n[0], 393 full_path, 2047, full_n) 394 if jv != NXP_OK { return -1 } 395 full_path[full_n[0]] = 0 as u8 396 397 // Step 4: read body. 398 let body_n_p: *i64 = sys_mmap(8) as *i64 399 let body: *u8 = sys_read_file(full_path, body_n_p) 400 if body == (0 as *u8) { 401 // 404 402 let r404b: *u8 = "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n\r\nROUTED-404\r\n\r\n" as *u8 403 var rn4: i64 = 0 404 while rn4 < 87 { 405 if off >= resp_cap { return -1 } 406 resp_buf[off] = r404b[rn4] 407 off = off + 1 408 rn4 = rn4 + 1 409 } 410 return off 411 } 412 let body_n: i64 = body_n_p[0] 413 414 // Step 5: compute ETag. 415 let etag_buf: *u8 = sys_mmap(32) 416 let etag_off: *i64 = sys_mmap(8) as *i64 417 etag_off[0] = 0 418 nx_http_etag_compute(body, body_n, etag_buf, etag_off, 32) 419 let etag_n: i64 = etag_off[0] 420 421 // Step 6: If-None-Match check. 422 if inm_rc == NXHF_FOUND { 423 let inm_ptr: *u8 = ((req_buf as i64) + inm_off[0]) as *u8 424 let decision: i64 = nx_http_cache_decide(inm_ptr, inm_len[0], 425 etag_buf, etag_n) 426 if decision == NXC_OK_NOT_MODIFIED { 427 // 304: ~113 bytes total 428 let r304_off: *i64 = sys_mmap(8) as *i64 429 r304_off[0] = 0 430 nx_http_emit_304(resp_buf, r304_off, resp_cap, 431 etag_buf, etag_n, 300) 432 *served_304 = 1 433 return r304_off[0] 434 } 435 } 436 437 // Step 7: emit 200 with full S-class header set. 438 // Detect MIME from canonical path. 439 let mime: i64 = nx_pages_mime_from_path(canon_buf, canon_n[0]) 440 441 // Status + Content-Type 442 let h1: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: " as *u8 443 var hi: i64 = 0 444 while h1[hi] != 0 { 445 if off >= resp_cap { return -1 } 446 resp_buf[off] = h1[hi] 447 off = off + 1 448 hi = hi + 1 449 } 450 let mime_str: *u8 = nx_pages_mime_type(mime) 451 let mime_n: i64 = nx_pages_mime_type_len(mime) 452 var mi: i64 = 0 453 while mi < mime_n { 454 if off >= resp_cap { return -1 } 455 resp_buf[off] = mime_str[mi] 456 off = off + 1 457 mi = mi + 1 458 } 459 if off + 2 > resp_cap { return -1 } 460 resp_buf[off] = 0x0d as u8; off = off + 1 461 resp_buf[off] = 0x0a as u8; off = off + 1 462 463 // ETag 464 let et_pre: *u8 = "ETag: \"" as *u8 465 var ei: i64 = 0 466 while et_pre[ei] != 0 { 467 if off >= resp_cap { return -1 } 468 resp_buf[off] = et_pre[ei] 469 off = off + 1 470 ei = ei + 1 471 } 472 var eb: i64 = 0 473 while eb < etag_n { 474 if off >= resp_cap { return -1 } 475 resp_buf[off] = etag_buf[eb] 476 off = off + 1 477 eb = eb + 1 478 } 479 let et_post: *u8 = "\"\r\n" as *u8 480 var ep: i64 = 0 481 while et_post[ep] != 0 { 482 if off >= resp_cap { return -1 } 483 resp_buf[off] = et_post[ep] 484 off = off + 1 485 ep = ep + 1 486 } 487 488 // Cache-Control + Security headers (bundled) 489 let extras_off: *i64 = sys_mmap(8) as *i64 490 extras_off[0] = off 491 nx_http_emit_cache_control_header(resp_buf, extras_off, resp_cap, 300) 492 nx_http_emit_security_headers_bundle(resp_buf, extras_off, resp_cap, NXSH_STRICT) 493 off = extras_off[0] 494 495 // Content-Length: <body_n>\r\n 496 let cl_pre: *u8 = "Content-Length: " as *u8 497 var ci: i64 = 0 498 while cl_pre[ci] != 0 { 499 if off >= resp_cap { return -1 } 500 resp_buf[off] = cl_pre[ci] 501 off = off + 1 502 ci = ci + 1 503 } 504 // Decimal body_n 505 var bn: i64 = body_n 506 if bn == 0 { 507 if off >= resp_cap { return -1 } 508 resp_buf[off] = 0x30 as u8 509 off = off + 1 510 } else { 511 let dig_start: i64 = off 512 while bn > 0 { 513 if off >= resp_cap { return -1 } 514 resp_buf[off] = (0x30 + (bn - (bn / 10) * 10)) as u8 515 off = off + 1 516 bn = bn / 10 517 } 518 var lo: i64 = dig_start 519 var hi2: i64 = off - 1 520 while lo < hi2 { 521 let tmp: i64 = resp_buf[lo] as i64 522 resp_buf[lo] = resp_buf[hi2] 523 resp_buf[hi2] = tmp as u8 524 lo = lo + 1 525 hi2 = hi2 - 1 526 } 527 } 528 if off + 4 > resp_cap { return -1 } 529 resp_buf[off] = 0x0d as u8 530 resp_buf[off + 1] = 0x0a as u8 531 resp_buf[off + 2] = 0x0d as u8 532 resp_buf[off + 3] = 0x0a as u8 533 off = off + 4 534 535 // Body 536 var bi: i64 = 0 537 while bi < body_n { 538 if off >= resp_cap { return -1 } 539 resp_buf[off] = body[bi] 540 off = off + 1 541 bi = bi + 1 542 } 543 return off 544} 545 546// ---- Main daemon loop -------------------------------------------- 547 548func main() -> i64 { 549 // Bind 127.0.0.1:51847 550 let addr: *u8 = sys_mmap(16) 551 let a_rc: i64 = nx_http_server_addr_loopback(addr, NXAR_PORT) 552 if a_rc != 16 { return 10 } 553 554 let lv: *i64 = sys_mmap(8) as *i64 555 let lfd: i64 = nx_http_server_listen(addr, 64, lv) 556 if lfd < 0 { return 20 + lv[0] } 557 558 // Start clock (monotonic ms). 559 let t_start: i64 = sys_now_ms() 560 561 // Internal SYSTEM_PERF counters per S14 (no PEOPLE_* fields). 562 var n_requests: i64 = 0 563 var n_errors: i64 = 0 564 var n_served: i64 = 0 565 566 let base_dir: *u8 = "docs" as *u8 567 let base_dir_n: i64 = 4 568 569 while n_served < NXAR_REQUEST_BUDGET { 570 let av: *i64 = sys_mmap(8) as *i64 571 let cfd: i64 = nx_http_server_accept_one(lfd, av) 572 if cfd < 0 { 573 n_errors = n_errors + 1 574 n_served = n_served + 1 575 if n_served >= NXAR_REQUEST_BUDGET { return 0 } 576 } 577 578 if cfd >= 0 { 579 // HTTP/1.1 keepalive: read multiple requests per accepted 580 // TCP connection up to NXAR_PER_CONN_BUDGET. Browser 581 // pages reuse one TCP connection for HTML + linked 582 // assets; nginx + WordPress + every other production 583 // stack does this. Substrate's parity. 584 var reqs_on_conn: i64 = 0 585 var conn_alive: i64 = 1 586 while conn_alive == 1 && reqs_on_conn < NXAR_PER_CONN_BUDGET && n_served < NXAR_REQUEST_BUDGET { 587 let req_buf: *u8 = sys_mmap(NXAR_REQ_CAP) 588 let om: *i64 = sys_mmap(8) as *i64 589 let opo: *i64 = sys_mmap(8) as *i64 590 let opl: *i64 = sys_mmap(8) as *i64 591 let ocl: *i64 = sys_mmap(8) as *i64 592 let obo: *i64 = sys_mmap(8) as *i64 593 let orn: *i64 = sys_mmap(8) as *i64 594 595 let rrc: i64 = nx_http_server_read_request(cfd, req_buf, NXAR_REQ_CAP, 596 om, opo, opl, ocl, obo, orn) 597 if rrc != NXS_OK { 598 // EOF or parse error -- client closed or bad request. 599 conn_alive = 0 600 } 601 if rrc == NXS_OK { 602 let path_ptr: *u8 = ((req_buf as i64) + opo[0]) as *u8 603 let path_n: i64 = opl[0] 604 let resp_buf: *u8 = sys_mmap(NXAR_RESP_CAP) 605 let resp_n: *i64 = sys_mmap(8) as *i64 606 resp_n[0] = 0 607 var off: i64 = 0 608 609 let now_ms: i64 = sys_now_ms() 610 let uptime_ms: i64 = now_ms - t_start 611 let budget_remaining: i64 = NXAR_REQUEST_BUDGET - n_served 612 613 // Dispatch. 614 var handled: i64 = 0 615 if nxar_path_eq(path_ptr, path_n, "/health" as *u8, 7) == 1 { 616 nxar_serve_health(resp_buf, NXAR_RESP_CAP, resp_n, 617 uptime_ms, budget_remaining) 618 handled = 1 619 } 620 if handled == 0 { 621 if nxar_path_eq(path_ptr, path_n, "/ready" as *u8, 6) == 1 { 622 nxar_serve_health(resp_buf, NXAR_RESP_CAP, resp_n, 623 uptime_ms, budget_remaining) 624 handled = 1 625 } 626 } 627 if handled == 0 { 628 if nxar_path_eq(path_ptr, path_n, "/metrics" as *u8, 8) == 1 { 629 nxar_serve_metrics(resp_buf, NXAR_RESP_CAP, resp_n, 630 uptime_ms, n_requests, n_errors, 631 budget_remaining) 632 handled = 1 633 } 634 } 635 if handled == 0 { 636 if nxar_path_starts_with(path_ptr, path_n, "/audit" as *u8, 6) == 1 { 637 // S-class flow: ETag + Cache-Control + 638 // security headers + If-None-Match -> 304. 639 let s304: *i64 = sys_mmap(8) as *i64 640 let new_n: i64 = nxar_serve_audit_cached( 641 req_buf, obo[0], 642 path_ptr, path_n, 643 base_dir, base_dir_n, 644 resp_buf, NXAR_RESP_CAP, 645 s304) 646 if new_n > 0 { resp_n[0] = new_n } 647 handled = 1 648 } 649 } 650 if handled == 0 { 651 if nxar_path_eq(path_ptr, path_n, "/" as *u8, 1) == 1 { 652 nxar_emit_root(resp_buf, &off, NXAR_RESP_CAP) 653 resp_n[0] = off 654 handled = 1 655 } 656 } 657 if handled == 0 { 658 nxar_emit_404(resp_buf, &off, NXAR_RESP_CAP) 659 resp_n[0] = off 660 n_errors = n_errors + 1 661 } 662 663 // Keepalive variant: don't close fd here so we can 664 // serve another request on the same connection. 665 let send_rc: i64 = nx_http_server_send_response_nokeep_close( 666 cfd, resp_buf, resp_n[0]) 667 if send_rc != NXS_OK { conn_alive = 0 } 668 n_requests = n_requests + 1 669 670 // SYSTEM-class request log (no IP / UA / user data per S14). 671 var log_status: i64 = 200 672 if handled == 0 { log_status = 404 } 673 let req_end_ms: i64 = sys_now_ms() 674 let req_dur: i64 = req_end_ms - now_ms 675 nxar_log_request(now_ms, om[0], path_ptr, path_n, 676 log_status, req_dur, resp_n[0]) 677 } 678 n_served = n_served + 1 679 reqs_on_conn = reqs_on_conn + 1 680 } // close inner keepalive loop 681 sys_close(cfd) 682 } 683 } 684 685 sys_close(lfd) 686 return 0 687}