04 / RESILIENCE
limits
The timeout middleware, which starts a moonward Deadline per request and enforces it on the response path (preemptive cancel is the async boundary), and maxbytes, which rejects an over-limit Content-Length with 413.
WHERE IT SITS
INTERFACE
fn timeout( budget_ms : Int64, clock : @clock.Clock, refusal~ : Refusal = timed_out) -> MiddlewarefnTimeout middleware: start a moonward Deadline of budget_ms at clock.now() for each request to the wrapped app. **Async boundary (faithful model).** *Preemptively* aborting an in-flight handler the instant its deadline fires requires racing the handler against a timer and cancelling the loser — in MoonBit that is @async.any([handler, timer]) with structured cancellation, which only runs under the native async runtime and cannot be driven synchronously. What this middleware does portably: it installs the deadline and enforces it on the response path — if the handler blows its budget before emitting its first event, the client receives a 503 timeout (from timeout_events) and the late response is suppressed. The remaining gap (a handler that hangs and never emits) is closed by the race/cancel wired at the async server edge. A budget_ms <= 0 disables the timeout, passing straight through.
fn maxbytes(limit : Int32, refusal~ : Refusal = too_large) -> MiddlewarefnMax-bytes middleware: reject any HTTP request whose declared Content-Length exceeds limit bytes with 413 Payload Too Large, before the wrapped app runs. A limit <= 0 disables the check. Non- HTTP scopes pass through untouched.