code wiki / (root) / nx_deadline_lib.nx

nx_deadline_lib.nx source

↩ module page · 173 lines · 9164 B

1// nx_deadline_lib.nx -- REQUEST DEADLINE PROPAGATION: the CONSUMER half of X-Nishi-Deadline-Mono-Ms. 2// 3// WHY IT EXISTS, and it is a defect I created myself on 2026-09-03. The front door now stamps 4// `X-Nishi-Deadline-Mono-Ms: <monotonic ms>` on every buffered proxy hop -- and NOTHING READ IT. That is 5// the estate's own law firing on its author: ★AN ENVELOPE MEASURED, PRINTED, AND THEN DROPPED AT THE NEXT 6// HOP IS AN ENVELOPE THAT WAS NEVER PUBLISHED. A producer with no consumer is not half a feature, it is 7// zero of one, and it reads as progress on every board. 8// 9// WHY IT DOES NOT PARSE HEADERS. `gw_hdr_val` is byte-identical in FIVE gateway sources 10// (sha 9410a9c6...), so writing a sixth copy here would add a duplicate ruler to fix a duplicate ruler. 11// This lib takes the VALUE and answers the QUESTION; each backend keeps whatever header reader it already 12// has. ★SEPARATE THE PARSER FROM THE POLICY AND THE POLICY BECOMES ADOPTABLE BY EVERY BACKEND AT ONCE. 13// 14// WHY THE CLAMP IS THE WHOLE POINT -- IT COMPOSES, IT DOES NOT REPLACE. The estate already has a timeout 15// ruler per lane (`pr_lookup_tmo` and friends), and the standing instruction for this rung is to COMPOSE 16// it rather than add a second. So `dl_wait_ms` NEVER returns more than the caller's own `want`: it can 17// only ever SHORTEN a wait the caller already chose. That direction is load-bearing -- 18// ★A DEADLINE PRIMITIVE THAT CAN EXTEND A CALLER'S TIMEOUT IS A SECOND TIMEOUT RULER WEARING A 19// DEADLINE'S CLOTHES, AND THE TWO WILL DISAGREE THE FIRST TIME EITHER IS TUNED. 20// With that property, "honouring the deadline" is BY CONSTRUCTION for any caller that routes its wait 21// through this function, and adoption is one call rather than a policy everyone must remember. 22// 23// THE CLOCK IS MONOTONIC AND PER-BOOT, WHICH IS WHY THE HEADER SAYS SO IN ITS NAME. sys_now_ms is 24// CLOCK_MONOTONIC: comparable only within one boot of one host. That is exactly right for an edge and a 25// backend on the SAME box and exactly wrong across machines, so the name carries the constraint where a 26// reader cannot miss it -- a cross-host caller must not use this without a wall-clock variant. 27// license_tier: ORIGINAL Read-only. No hw writes (Rule 26). lib (no main) 28import "nx_syscalls.nx" 29 30// THE HEADER NAME LIVES HERE, ONCE. The producer (nx_sites_daemon_v2's NX_SD2_DEADLINE_HDR) carries its 31// own copy today; nx_deadline_gate asserts the two are EQUAL by reading the producer's source, so a 32// divergence cannot ship silently even while two copies exist. ★WHEN TWO ORGANS MUST AGREE AND CANNOT YET 33// SHARE A CONSTANT, MAKE THE DISAGREEMENT A RED GATE RATHER THAN A COMMENT ASKING PEOPLE TO BE CAREFUL. 34const DL_HDR: *u8 = "X-Nishi-Deadline-Mono-Ms: " 35 36const DL_ABSENT: i64 = 0 - 1 // no parseable deadline in the value 37 38// states, named so a caller can never confuse "no deadline" with "deadline already gone" 39const DL_S_NONE: i64 = 0 // the request declared no deadline -- do NOT invent one 40const DL_S_LIVE: i64 = 1 // a deadline exists and has time left 41const DL_S_EXPIRED: i64 = 2 // a deadline exists and has passed 42 43func dl_state_name(s: i64) -> *u8 { 44 if s == DL_S_NONE { return "NO-DEADLINE-DECLARED" as *u8 } 45 if s == DL_S_LIVE { return "LIVE" as *u8 } 46 return "EXPIRED" as *u8 47} 48 49// Parse a decimal monotonic-ms deadline out of a header VALUE. Returns DL_ABSENT for an empty value, for 50// one with no leading digits, and for one that would overflow -- never a partial number silently treated 51// as a deadline. Stops at the first non-digit, so a trailing "\r" or " ; q=1" is harmless. 52// ⚠ABSENT AND ZERO ARE DIFFERENT ANSWERS: a literal "0" is a real (long-expired) deadline, while a 53// missing header is a caller who never asked for one, and conflating them would silently impose a 54// zero-budget deadline on every request that lacks the header. 55func dl_parse_ms(val: *u8, n: i64) -> i64 { 56 if n <= 0 { return DL_ABSENT } 57 var i: i64 = 0 58 // skip one optional leading space, as a header value may carry it 59 if val[0] == (32 as u8) { i = 1 } 60 var seen: i64 = 0 61 var v: i64 = 0 62 var go: i64 = 1 63 while go == 1 { 64 if i >= n { go = 0 } else { 65 let c: i64 = val[i] as i64 66 if c < 48 { go = 0 } else { 67 if c > 57 { go = 0 } else { 68 // refuse rather than wrap: a wrapped deadline is a wrong answer with a plausible shape 69 if v > 900000000000000 { return DL_ABSENT } 70 v = v * 10 + (c - 48) 71 seen = 1 72 i = i + 1 73 } 74 } 75 } 76 } 77 if seen == 0 { return DL_ABSENT } 78 return v 79} 80 81// ---- FINDING THE DEADLINE. This lives HERE, not in a daemon, because every backend that honours a 82// deadline must first FIND one -- and five gateways carrying byte-identical copies of gw_hdr_val are the 83// estate's standing proof of what happens when a small helper is copied once per consumer. 84// It deliberately does NOT parse a general header set (gw_hdr_val already does that, in five places). It 85// answers exactly one question: what deadline did this request arrive carrying? 86 87// The key length is DERIVED from DL_HDR, never hand-counted. A HAND-COUNTED LENGTH BESIDE A STRING 88// LITERAL IS A SECOND COPY OF THAT LITERAL'S SHAPE, AND THE TWO DRIFT SILENTLY -- rename the header, 89// forget the number, and the parser reads the wrong window while still compiling and still looking fine. 90func dl_keylen() -> i64 { 91 var i: i64 = 0 92 while DL_HDR[i] != (0 as u8) { 93 if (DL_HDR[i] as i64) == 58 { return i + 1 } 94 i = i + 1 95 } 96 return i 97} 98func dl_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c } 99 100// Case-INSENSITIVE, because HTTP field names are (RFC 9110 s5.1). Two consequences, both wanted: a hop 101// that re-cased the header still has its deadline honoured, and a duplicate it left behind is still 102// recognisable to a rewriter that means to strip it. 103func dl_is_hdr_line(buf: *u8, p: i64, n: i64) -> i64 { 104 let kl: i64 = dl_keylen() 105 var i: i64 = 0 106 while i < kl { 107 if p + i >= n { return 0 } 108 if dl_lc(buf[p+i] as i64) != dl_lc(DL_HDR[i] as i64) { return 0 } 109 i = i + 1 110 } 111 return 1 112} 113 114// The deadline this request ARRIVED with, or DL_ABSENT when it declared none. 115// HEADER BLOCK ONLY, and that bound is load-bearing rather than tidy: the scan stops at the blank line, 116// so a deadline-shaped string inside a POST body can never be read as a header. On a public front door the 117// body is attacker-controlled, and while a request that could SHORTEN its own deadline is merely odd, one 118// that could LENGTHEN it is a bypass of the very budget this file exists to enforce. 119// FIRST match wins -- the same rule every first-match header reader in the estate already follows, so a 120// duplicated header resolves here exactly as it resolves downstream. 121func dl_scan_request(buf: *u8, n: i64) -> i64 { 122 var he: i64 = 0 - 1 123 var i: i64 = 0 124 while i + 3 < n { 125 if (buf[i] as i64)==13 { if (buf[i+1] as i64)==10 { if (buf[i+2] as i64)==13 { if (buf[i+3] as i64)==10 { he = i + 4 } } } } 126 if he >= 0 { i = n } else { i = i + 1 } 127 } 128 var hend: i64 = n 129 if he >= 0 { hend = he - 2 } 130 let kl: i64 = dl_keylen() 131 var p: i64 = 0 132 while p < hend { 133 var le: i64 = p 134 var found: i64 = 0 135 while found == 0 { 136 if le + 1 >= hend { le = hend; found = 1 } 137 else { if (buf[le] as i64)==13 { if (buf[le+1] as i64)==10 { found = 1 } else { le = le + 1 } } else { le = le + 1 } } 138 } 139 if dl_is_hdr_line(buf, p, n) == 1 { 140 let vs: i64 = p + kl 141 if vs <= le { return dl_parse_ms(((buf as i64) + vs) as *u8, le - vs) } 142 } 143 p = le + 2 144 } 145 return DL_ABSENT 146} 147// milliseconds left. Meaningless unless the deadline came from dl_parse_ms on THIS host's boot. 148func dl_remaining_ms(deadline_ms: i64, now_ms: i64) -> i64 { 149 if deadline_ms == DL_ABSENT { return DL_ABSENT } 150 return deadline_ms - now_ms 151} 152 153func dl_state(deadline_ms: i64, now_ms: i64) -> i64 { 154 if deadline_ms == DL_ABSENT { return DL_S_NONE } 155 if deadline_ms <= now_ms { return DL_S_EXPIRED } 156 return DL_S_LIVE 157} 158 159// THE CLAMP. Returns how long the caller may actually wait, given the wait it already wanted. 160// no deadline -> want, UNCHANGED (never invent a budget the request did not ask for) 161// expired -> 0 (do not start work that cannot be delivered; the caller decides what 0 means) 162// live -> min(want, remaining) 163// It can only ever SHORTEN. That is what makes it a composition of the caller's own timeout rather than a 164// rival to it, and it is why adopting this needs no coordination with whatever ruler set `want`. 165func dl_wait_ms(deadline_ms: i64, now_ms: i64, want_ms: i64) -> i64 { 166 if want_ms < 0 { return 0 } 167 let st: i64 = dl_state(deadline_ms, now_ms) 168 if st == DL_S_NONE { return want_ms } 169 if st == DL_S_EXPIRED { return 0 } 170 let rem: i64 = deadline_ms - now_ms 171 if rem < want_ms { return rem } 172 return want_ms 173}