moonzero · control plane v0.15.0 · 49 packages · 4 targets · Apache-2.0 docs / edge / middleware GitHub

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).

fn layer(l : &@mid.Layer) -> Middleware

fnAdapt 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.AsgiApp

fnRecovery 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 CorsConf

structCORS 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) -> CorsConf

fnA permissive CORS config: any origin, method and header sets, and a one-day preflight cache.

fn cors_allows(allows : Array[String], origin : String) -> Bool

fnWhether 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) -> Middleware

fnCORS 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") -> Middleware

fnRequest-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.AsgiApp

typeAn AsgiApp transformer — one layer of the middleware onion.

fn buffered_body(body : Bytes) -> @moonasgi.Receive

fnA 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) -> Bytes

itemDrain 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.AsgiApp

fnA 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.Layer

fnThis middleware as a mid layer, for a stack built by @mid.stack.

fn of_layer(layer : &@mid.Layer) -> Middleware

fnA mid layer as a middleware here, so a layer that package ships can sit in this service's chain.