02 / EDGE
middleware
The onion layers that wrap the app: recovery (500 instead of a panic), cors (Access-Control-* headers), and request_id (x-request-id per request).
WHERE IT SITS
INTERFACE
fn layer(l : &@mid.Layer) -> MiddlewarefnAdapt a moonasgi mid.Layer into a moonzero Middleware. The two shapes are the same ASGI transformer, so this is the whole bridge — a layer written for moonasgi (cors, gzip, body_limit, server_errors, trusted_hosts, sessions, or a user's own) drops into a moonzero chain unchanged.
fn recovery(inner : @moonasgi.AsgiApp) -> @moonasgi.AsgiAppfnRecovery middleware: run the wrapped application inside a try, and if it raises before any part of the response has gone out, emit a 500 Internal Server Error instead of letting the failure escape to the server. Once the response has started, a 500 can no longer be written — the status line is already on the wire — so the error is re-raised for the server to close the connection on. Answering late would append a second status to a response the client is already reading.
struct CorsConfstructCORS configuration: which origins may call the service, and what a preflight is told about the rest of the policy.
let cors_methods : String = "GET, HEAD, POST, PATCH, PUT, DELETE"letThe method set advertises.
let cors_headers : String = "Content-Type, Origin, X-CSRF-Token, Authorization, AccessToken, Token, Range"letThe request headers lets through a preflight.
let cors_expose : String = "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers"letThe response headers exposes to script.
fn CorsConf::new( origins~ : Array[String] = ["*"], methods~ : String = cors_methods, headers~ : String = cors_headers, expose~ : String = cors_expose, max_age~ : Int32 = 86400) -> CorsConffnA permissive CORS config: any origin, method and header sets, and a one-day preflight cache.
fn cors_allows(allows : Array[String], origin : String) -> BoolfnWhether origin is allowed by allows: an exact match, a subdomain of an allowed origin, or a wildcard. Matching is case-insensitive on both sides, as the origin comparison in the Fetch standard is.
fn cors(conf : CorsConf) -> MiddlewarefnCORS middleware: answer a preflight OPTIONS with 204 No Content and the policy headers, and stamp the same headers onto every other response. A request from an origin the policy does not allow is served without the Access-Control-* headers rather than refused — the browser is the one that enforces the policy, and a server-side refusal would break non-browser clients that never asked for CORS at all.
fn scope_header(scope : @moonasgi.Scope, name : String) -> String?fnRead a header from an HTTP scope (lowercased-name convention); None for non-HTTP scopes or a missing header.
fn request_id(header~ : String = "x-request-id") -> MiddlewarefnRequest-ID middleware: reuse an inbound x-request-id if the client sent one, otherwise mint a fresh monotonic id, and stamp it onto every response's HttpResponseStart. The counter is captured once per assembly, so ids stay unique across the requests this layer serves.
type Middleware = (@moonasgi.AsgiApp) -> @moonasgi.AsgiApptypeAn AsgiApp transformer — one layer of the middleware onion.
fn buffered_body(body : Bytes) -> @moonasgi.ReceivefnA receive channel that hands the application body once and then reports the connection closed — the shape an ASGI application sees for a body a layer has already buffered.
async fn read_body(receive : @moonasgi.Receive) -> BytesitemDrain a receive channel into one body. Used by the layers that have to read the whole payload before they can decide anything.
fn logging(inner : @moonasgi.AsgiApp) -> @moonasgi.AsgiAppfnA request-logging middleware: writes METHOD path through the process logx at info for each HTTP request, then delegates to the wrapped application. It logs on the way in and knows nothing of the response; structured_logging is the layer that times the request and records its status.
extend AsLayer with @mid.Layer::item
fn as_layer(middleware : Middleware) -> &@mid.LayerfnThis middleware as a mid layer, for a stack built by @mid.stack.
fn of_layer(layer : &@mid.Layer) -> MiddlewarefnA mid layer as a middleware here, so a layer that package ships can sit in this service's chain.