API reference

Every public item of every package, in the order the compiler lists them. Generated from the package interfaces by scripts/docs/gen_api.py; the prose is the /// comment beside each declaration.

mooncat25 items

The entry points, re-exported: what a program that serves one application needs, and nothing it does not.

Values

fnfn http_date(Int64) -> String

Format a Unix-epoch millisecond count as an HTTP-date in the IMF-fixdate form RFC 7231 §7.1.1.1 makes mandatory for a Date header: Sun, 06 Nov 1994 08:49:37 GMT, always GMT, always fixed-width.

The calendar and the format are moondate's; what is here is the millisecond clock mooncat reads, brought down to the whole second the header is written to.

fnfn http_date_now() -> String

The current instant as an HTTP-date, reformatted only when the epoch second has moved on.

letlet port : Int32

The default port for the TCP serving entry points.

HTTP/1.1, WebSocket upgrades and HTTP/2 share one listener, told apart by ALPN under TLS and by the HTTP/2 preface in the clear. quic_serve takes an explicit UDP address; it may use the same number because UDP and TCP have separate port spaces.

fnfn proxy_rewrite(Map[String, String], (String, Int32)?, String, trusted~ : Array[String], websocket? : Bool) -> ((String, Int32)?, String)

Rewrite a request's client and scheme from a trusted proxy's forwarding headers (← uvicorn's ProxyHeadersMiddleware).

Nothing is rewritten unless the peer itself is trusted: these headers are attacker-controlled, and believing them from an arbitrary client lets that client name any address it likes for a rate limiter, an allowlist or an audit log. Behind a proxy the peer is the proxy, which is why forwarded_allow_ips defaults to 127.0.0.1 and not to *.

X-Forwarded-For yields the chain's leftmost entry with port 0, since a forwarded chain carries no port. X-Forwarded-Proto replaces the scheme, mapped to wss/ws for a WebSocket scope, whose scheme names differ from the https/http the header spells.

fnasync fn read_frame(&@io.Reader, client? : Bool, limit? : Int32) -> @ws.Frame

One frame off a stream (RFC 6455 §5.2).

The header is read in the order the format is laid out — two bytes, then the extended length, then the mask — and @ws.check runs on the header before the payload is read, so an enormous announced length is refused rather than allocated.

client is the server's position by default: a client's frames must be masked (§5.1), so reading a server's frames takes client=false. Raises @ws.Refused carrying the status the connection is then failed with, and passes the stream's end through.

fnasync fn read_message(&@io.Reader, &@io.Writer, limit? : Int32) -> @ws.Message

One complete message off a stream, with the control frames between its fragments handled: a ping is answered on writer, a pong is dropped, and a close is echoed before the message comes back as that close.

This is the server's receive path, so every frame must arrive masked. The reply frames go out unmasked because a server must not mask (§5.1).

fnasync fn reload_watch(String, async () -> Unit) -> Unit

Watch path for source changes and fire on_reload on each batch of events (← uvicorn's --reload file watcher). Backed by @fs.Watcher, which debounces and reports child-file events, so a save to any file under path triggers one reload. on_reload is where a supervisor re-execs the server; wiring it to a ShutdownHandle turns a file save into a graceful restart. Loops until its task is cancelled, always closing the watcher.

fnasync fn serve(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, host? : String, port? : Int32, backlog? : Int32) -> Unit

Serve under a plan already parsed, for a program that wants to look at the plan — or change it — before the socket opens.

fnasync fn serve_config(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config) -> Unit

Serve HTTP/1.1, HTTP/2 and HTTP/3 together under one certificate, TCP and UDP on the same port: the TCP side tells clients about HTTP/3 with alt-svc (RFC 7838).

With workers above 1 the TCP port is shared by the kernel, as for serve_tls, and the UDP port by this process: it holds the socket and passes each datagram to the worker whose number is in the connection id (see route.mbt).

fnasync fn serve_graceful(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config, handle? : @serve.ShutdownHandle) -> Unit

Serve a moonasgi application with a uvicorn-style process model: a graceful shutdown path over a single acceptor that spawns a concurrent handler per connection.

The lifespan protocol runs startup before the listener binds and shutdown on the way out. A single acceptor task spawns one handler task per connection, so requests are served concurrently, each through mooncat's HTTP/1.1 codec and, on an upgrade, its WebSocket bridge. serve, serve_config and serve_tls_config run this same loop; this entry point adds the handle.

Shutdown is triggered by handle.shutdown() / handle.request_stop(), by reaching Config::limit_max_requests, or by a signal the runtime turns into global cancellation (see the boundary note below). All three converge on one sequence, run under protect_from_cancel so a signal can't abort it midway: stop accepting (cancel the acceptor), drain in-flight requests (bounded by Config::graceful_timeout), run the ASGI lifespan shutdown, then close the listener.

The acceptor honours the connection ceilings: max_connections gates accepting, so a full server leaves the next client in the listen queue rather than accepting it and sitting on it, and allow_failure decides whether one bad connection is contained or brings the server down. limit_concurrency is the other kind of ceiling — a request arriving past it is answered 503 rather than queued.

## Several workers

With Config::workers above 1 this process only supervises: see supervise. Each worker comes back through this same function, binds the port under SO_REUSEPORT, and serves as described here.

## Signal boundary

The only signal hook moonbitlang/async exposes is @signal.set_global_cancellation_signals, which cancels the whole task tree on SIGINT/SIGTERM. mooncat catches that cancellation and still runs lifespan shutdown and closes the listener under protect_from_cancel; but a signal also cancels the in-flight handler tasks, so drain-before-close is only fully honoured on the programmatic ShutdownHandle path. That matches uvicorn's own escalation: a first signal drains, a second forces exit.

fnasync fn serve_h2c(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, host? : String, port? : Int32) -> Unit

Serve a moonasgi ASGI application over HTTP/2 cleartext (h2c) only — the prior-knowledge, no-TLS HTTP/2 profile (RFC 9113 §3.3). Convenience wrapper over serve_h2c_config that builds a Config from host / port. Blocks in the accept loop until the running task is cancelled.

serve and serve_tls speak HTTP/2 as well, beside HTTP/1.1 on the same port; this entry point is for a listener that is to speak nothing else.

fnasync fn serve_h2c_config(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config) -> Unit

Serve a moonasgi ASGI application over h2c under an explicit Config: the same serving loop as serve_config, with its lifespan, limits, logging and graceful shutdown, on a listener that speaks HTTP/2 alone.

fnasync fn serve_tls(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, certificate_file~ : String, private_key_file~ : String, host? : String, port? : Int32, backlog? : Int32) -> Unit

Serve a moonasgi ASGI application over native HTTP/1.1 over TLS (HTTPS, ← uvicorn's --ssl-certfile/--ssl-keyfile). Convenience wrapper over serve_tls_config that builds the Config from host/port/backlog and the certificate paths.

Runs the full ASGI lifespan protocol around the accept loop (startup before the listener binds, shutdown on exit — even under cancellation), then, for every accepted connection, completes a TLS handshake and serves HTTP/2 when ALPN settles on h2, HTTP/1.1 otherwise.

A WebSocket upgrade on an HTTP/1.1 connection is served too (wss://): the 101, the subprotocol echo and the frames all travel over the encrypted stream.

fnasync fn serve_tls_beside(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config, @tls.Server, async (@serve.Site) -> Unit) -> Unit

Serve over HTTPS with another listener beside the TCP one, which runs from the moment the application has started until the requests in flight everywhere have drained.

fnasync fn serve_tls_config(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config, @serve.TlsCert) -> Unit

Serve a moonasgi ASGI application over HTTPS under an explicit Config and TlsCert. Mirrors serve_config: lifespan startup is driven before the listener binds and shutdown on the way out (guarded by protect_from_cancel so it still runs when the serving task is cancelled), and each accepted connection completes a TLS handshake within timeout_handshake and then runs the same loop as a plain one.

fnasync fn serve_tls_server(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config, @tls.Server) -> Unit

Serve over HTTPS with a TLS configuration built by the caller: the suites, the groups, the ALPN list and the certificates are all server's. serve_tls_config is this with the configuration read from PEM files.

Type aliases

aliasusing @config {type Config}

Re-exported from mooncat/config; documented there.

aliasusing @lifespan {type Driver}

Re-exported from mooncat/lifespan; documented there.

aliasusing @config {type H2Config}

Re-exported from mooncat/config; documented there.

aliasusing @config {type Lifespan}

Re-exported from mooncat/config; documented there.

aliasusing @lifespan {type LifespanError}

Re-exported from mooncat/lifespan; documented there.

aliasusing @config {type Logger}

Re-exported from mooncat/config; documented there.

aliasusing @serve {type ShutdownHandle}

Re-exported from mooncat/serve; documented there.

aliasusing @serve {type TlsCert}

Re-exported from mooncat/serve; documented there.

aliasusing @config {type WsConfig}

Re-exported from mooncat/config; documented there.

mooncat/config19 items

The configuration record, the access and error log, and the HTTP date every response can carry.

Values

fnfn http_date(Int64) -> String

Format a Unix-epoch millisecond count as an HTTP-date in the IMF-fixdate form RFC 7231 §7.1.1.1 makes mandatory for a Date header: Sun, 06 Nov 1994 08:49:37 GMT, always GMT, always fixed-width.

The calendar and the format are moondate's; what is here is the millisecond clock mooncat reads, brought down to the whole second the header is written to.

fnfn http_date_now() -> String

The current instant as an HTTP-date, reformatted only when the epoch second has moved on.

letlet port : Int32

The default port for the TCP serving entry points.

HTTP/1.1, WebSocket upgrades and HTTP/2 share one listener, told apart by ALPN under TLS and by the HTTP/2 preface in the clear. quic_serve takes an explicit UDP address; it may use the same number because UDP and TCP have separate port spaces.

fnfn reason(Int32) -> String

The reason phrase an access line pairs with a status. Lives here rather than beside the native server because the access line is written on every backend.

letlet ws_max_queue : Int32

How many received messages wait for the application (uvicorn's --ws-max-queue).

letlet ws_ping : Int32

The milliseconds between pings, and the wait for a pong (uvicorn's 20 seconds each).

Types and methods

fnfn Config::bind(Self) -> String

The host:port string used to resolve the listen address.

fnfn Config::new(host? : String, port? : Int32, backlog? : Int32, dual_stack? : Bool, reuse_addr? : Bool, server_headers? : Map[String, String], max_connections? : Int32, allow_failure? : Bool, graceful_timeout? : Int32, root_path? : String, logger? : Logger, timeout_keep_alive? : Int32, timeout_handshake? : Int32, limit_max_requests? : Int32, limit_concurrency? : Int32, max_head_size? : Int32, date_header? : Bool, proxy_headers? : Bool, forwarded_allow_ips? : Array[String], ws? : WsConfig, h2? : H2Config, lifespan? : Lifespan, cleartext? : Bool, workers? : Int32) -> Self

Build a Config, defaulting to uvicorn's own defaults: host 127.0.0.1, port 12000, backlog 2048, a 5-second idle keep-alive timeout, a 16 KiB request-head cap, the Date header on, proxy headers trusted from 127.0.0.1, and no request-count or concurrency limit. Transport knobs default to the async server's own defaults: reuse_addr on (uvicorn sets SO_REUSEADDR), single-stack binding, no extra response headers, an unbounded connection ceiling, and allow_failure on so a single failing handler never crashes the listener.

graceful_timeout bounds how long serve_graceful waits for in-flight requests to drain before it runs lifespan shutdown anyway (← uvicorn's timeout_graceful_shutdown); None waits until the last request finishes, as uvicorn does by default.

fnfn H2Config::new(max_streams? : Int32, max_header_list? : Int32, max_frame? : Int32, window? : Int32, connection_window? : Int32) -> Self

A H2Config with every field at its default: 100 concurrent streams, a 64 KiB field block, 16 KiB frames, and 1 MiB of flow-control window on a stream and on the connection.

fnfn Lifespan::equal(Self, Self) -> Bool

From the type's derive(Eq).

fnfn Lifespan::not_equal(Self, Self) -> Bool

From the type's derive(Eq).

fnfn Lifespan::to_repr(Self) -> @debug.Repr

From the type's derive(Debug).

structstruct Logger

How much the server says, and where it goes.

out : @moonlog.Logger
access : Bool
fnfn Logger::access_line(Self, String, String, String, String, Int32) -> Unit

Write the one-line-per-request access log (uvicorn's uvicorn.access), in the same shape: peer, request line, status.

fnfn Logger::enabled(Self, @moonlog.Level) -> Bool

Whether a line at level would be written.

fnfn Logger::log(Self, @moonlog.Level, String) -> Unit

Write one server-lifecycle line (uvicorn's uvicorn.error logger, which carries ordinary startup messages as well as failures).

fnfn Logger::new(level? : @moonlog.Level, access? : Bool, write? : (String) -> Unit) -> Self

A logger printing to stdout at level, with the access log on — uvicorn's defaults. access off silences the per-request lines while keeping the server's own.

fnfn Logger::silent() -> Self

A logger that says nothing, for an embedder that does its own reporting or a test that would rather not have output.

fnfn WsConfig::new(max_size? : Int32, max_queue? : Int32, ping_interval? : Int32?, ping_timeout? : Int32?, deflate? : Bool) -> Self

A WsConfig from uvicorn's defaults, max_size its 16 MiB.

mooncat/lifespan6 items

The lifespan protocol: an application invoked once before the listener binds and once on the way out.

Errors

errorsuberror LifespanError

Raised when the lifespan protocol stops the boot or reports a failed shutdown: the application sent lifespan.startup.failed or lifespan.shutdown.failed, or it raised under Lifespan::On. Carries the application's message.

LifespanError(String)

Types and methods

structstruct Driver

Drives an application's lifespan protocol (← uvicorn's LifespanOn).

One invocation of the application runs under a Lifespan scope for the server's whole life: startup sends lifespan.startup and waits for the answer, shutdown sends lifespan.shutdown. state is the scope's namespace; the server gives every request scope a shallow copy, so what startup stores there reaches each request.

mode is uvicorn's --lifespan: under Auto an application that fails on the lifespan scope, as one serving only HTTP does, is taken not to speak the protocol and the server carries on; under On that failure stops the boot; under Off nothing runs.

state : Map[String, Json]
fnfn Driver::new(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, mode? : @config.Lifespan, logger? : @config.Logger, state? : Map[String, Json]) -> Self

A driver for one application. spawn parks the application under a Lifespan scope, and startup / shutdown deliver the two messages to it in that order. state is what the startup writes down for the scopes that follow, which is why it is a parameter and not a field the caller sets afterwards.

fnasync fn Driver::shutdown(Self) -> Unit

Run lifespan shutdown: send lifespan.shutdown and wait for the answer, unless the invocation has already ended. Raises LifespanError on lifespan.shutdown.failed, and on a failure of the application under On.

fnfn Driver::spawn(Self, @async.TaskGroup[Unit]) -> Unit

Start the lifespan invocation as a task in g. It waits on receive() until startup and shutdown send it something; its failure is contained, and reported through them.

fnasync fn Driver::startup(Self) -> Unit

Run lifespan startup: send lifespan.startup and wait for the answer. Returns on lifespan.startup.complete, and when the application returns without answering. Raises LifespanError on lifespan.startup.failed, and on a failure of the application under On; under Auto such a failure means the application does not speak the protocol.

mooncat/proxy1 items

The client and scheme a trusted proxy reports, and the decision to believe it.

Values

fnfn proxy_rewrite(Map[String, String], (String, Int32)?, String, trusted~ : Array[String], websocket? : Bool) -> ((String, Int32)?, String)

Rewrite a request's client and scheme from a trusted proxy's forwarding headers (← uvicorn's ProxyHeadersMiddleware).

Nothing is rewritten unless the peer itself is trusted: these headers are attacker-controlled, and believing them from an arbitrary client lets that client name any address it likes for a rate limiter, an allowlist or an audit log. Behind a proxy the peer is the proxy, which is why forwarded_allow_ips defaults to 127.0.0.1 and not to *.

X-Forwarded-For yields the chain's leftmost entry with port 0, since a forwarded chain carries no port. X-Forwarded-Proto replaces the scheme, mapped to wss/ws for a WebSocket scope, whose scheme names differ from the https/http the header spells.

mooncat/serve29 items

The accept loop, HTTP/1.1, HTTP/2, WebSocket, TLS, workers and the graceful shutdown.

Values

letlet alpn : Array[String]

The application protocols a TLS listener offers, in preference order: HTTP/2 first, as hypercorn and nginx do, falling back to HTTP/1.1.

fnasync fn read_frame(&@io.Reader, client? : Bool, limit? : Int32) -> @ws.Frame

One frame off a stream (RFC 6455 §5.2).

The header is read in the order the format is laid out — two bytes, then the extended length, then the mask — and @ws.check runs on the header before the payload is read, so an enormous announced length is refused rather than allocated.

client is the server's position by default: a client's frames must be masked (§5.1), so reading a server's frames takes client=false. Raises @ws.Refused carrying the status the connection is then failed with, and passes the stream's end through.

fnasync fn read_message(&@io.Reader, &@io.Writer, limit? : Int32) -> @ws.Message

One complete message off a stream, with the control frames between its fragments handled: a ping is answered on writer, a pong is dropped, and a close is echoed before the message comes back as that close.

This is the server's receive path, so every frame must arrive masked. The reply frames go out unmasked because a server must not mask (§5.1).

fnasync fn reload_watch(String, async () -> Unit) -> Unit

Watch path for source changes and fire on_reload on each batch of events (← uvicorn's --reload file watcher). Backed by @fs.Watcher, which debounces and reports child-file events, so a save to any file under path triggers one reload. on_reload is where a supervisor re-execs the server; wiring it to a ShutdownHandle turns a file save into a graceful restart. Loops until its task is cancelled, always closing the watcher.

fnfn request_of(Array[@header.Header]) -> (String, String)

The method and target of a request head, for the access log.

fnasync fn serve(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, host? : String, port? : Int32, backlog? : Int32) -> Unit

Serve a moonasgi ASGI application over native HTTP/1.1 + WebSocket (← uvicorn). Convenience wrapper over serve_config that builds a Config from host / port / backlog. Blocks in a keep-alive accept loop until the running task is cancelled.

fnasync fn serve_config(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config) -> Unit

Serve a moonasgi ASGI application under an explicit Config.

Runs the full lifespan protocol around the accept loop: the app is invoked once under a Lifespan scope and startup() is driven before the listener is bound, and shutdown() is driven on the way out — even when the serving task is cancelled, via protect_from_cancel. Each accepted request is bridged by dispatch, which diverts WebSocket upgrades to the echo path.

fnasync fn serve_graceful(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config, handle? : ShutdownHandle) -> Unit

Serve a moonasgi application with a uvicorn-style process model: a graceful shutdown path over a single acceptor that spawns a concurrent handler per connection.

The lifespan protocol runs startup before the listener binds and shutdown on the way out. A single acceptor task spawns one handler task per connection, so requests are served concurrently, each through mooncat's HTTP/1.1 codec and, on an upgrade, its WebSocket bridge. serve, serve_config and serve_tls_config run this same loop; this entry point adds the handle.

Shutdown is triggered by handle.shutdown() / handle.request_stop(), by reaching Config::limit_max_requests, or by a signal the runtime turns into global cancellation (see the boundary note below). All three converge on one sequence, run under protect_from_cancel so a signal can't abort it midway: stop accepting (cancel the acceptor), drain in-flight requests (bounded by Config::graceful_timeout), run the ASGI lifespan shutdown, then close the listener.

The acceptor honours the connection ceilings: max_connections gates accepting, so a full server leaves the next client in the listen queue rather than accepting it and sitting on it, and allow_failure decides whether one bad connection is contained or brings the server down. limit_concurrency is the other kind of ceiling — a request arriving past it is answered 503 rather than queued.

## Several workers

With Config::workers above 1 this process only supervises: see supervise. Each worker comes back through this same function, binds the port under SO_REUSEPORT, and serves as described here.

## Signal boundary

The only signal hook moonbitlang/async exposes is @signal.set_global_cancellation_signals, which cancels the whole task tree on SIGINT/SIGTERM. mooncat catches that cancellation and still runs lifespan shutdown and closes the listener under protect_from_cancel; but a signal also cancels the in-flight handler tasks, so drain-before-close is only fully honoured on the programmatic ShutdownHandle path. That matches uvicorn's own escalation: a first signal drains, a second forces exit.

fnasync fn serve_h2c(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, host? : String, port? : Int32) -> Unit

Serve a moonasgi ASGI application over HTTP/2 cleartext (h2c) only — the prior-knowledge, no-TLS HTTP/2 profile (RFC 9113 §3.3). Convenience wrapper over serve_h2c_config that builds a Config from host / port. Blocks in the accept loop until the running task is cancelled.

serve and serve_tls speak HTTP/2 as well, beside HTTP/1.1 on the same port; this entry point is for a listener that is to speak nothing else.

fnasync fn serve_h2c_config(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config) -> Unit

Serve a moonasgi ASGI application over h2c under an explicit Config: the same serving loop as serve_config, with its lifespan, limits, logging and graceful shutdown, on a listener that speaks HTTP/2 alone.

fnasync fn serve_tls(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, certificate_file~ : String, private_key_file~ : String, host? : String, port? : Int32, backlog? : Int32) -> Unit

Serve a moonasgi ASGI application over native HTTP/1.1 over TLS (HTTPS, ← uvicorn's --ssl-certfile/--ssl-keyfile). Convenience wrapper over serve_tls_config that builds the Config from host/port/backlog and the certificate paths.

Runs the full ASGI lifespan protocol around the accept loop (startup before the listener binds, shutdown on exit — even under cancellation), then, for every accepted connection, completes a TLS handshake and serves HTTP/2 when ALPN settles on h2, HTTP/1.1 otherwise.

A WebSocket upgrade on an HTTP/1.1 connection is served too (wss://): the 101, the subprotocol echo and the frames all travel over the encrypted stream.

fnasync fn serve_tls_beside(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config, @tls.Server, async (Site) -> Unit) -> Unit

Serve over HTTPS with another listener beside the TCP one, which runs from the moment the application has started until the requests in flight everywhere have drained.

fnasync fn serve_tls_config(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config, TlsCert) -> Unit

Serve a moonasgi ASGI application over HTTPS under an explicit Config and TlsCert. Mirrors serve_config: lifespan startup is driven before the listener binds and shutdown on the way out (guarded by protect_from_cancel so it still runs when the serving task is cancelled), and each accepted connection completes a TLS handshake within timeout_handshake and then runs the same loop as a plain one.

fnasync fn serve_tls_server(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config, @tls.Server) -> Unit

Serve over HTTPS with a TLS configuration built by the caller: the suites, the groups, the ALPN list and the certificates are all server's. serve_tls_config is this with the configuration read from PEM files.

fnasync fn supervise(@config.Config, env? : Map[String, String]) -> Unit

Run config.workers workers until this process is stopped, replacing any that die. Stopping this process stops them, each given graceful_timeout to drain. env is added to every worker's environment, beside the variable that tells it its number.

fnfn worker() -> Int32?

This process's worker number when a supervisor started it, None in a process the user started.

Types and methods

structstruct ShutdownHandle

A handle for driving a graceful shutdown of serve_graceful from outside the serving task (← uvicorn's Server.should_exit / handle_exit). Hand one to serve_graceful, then call shutdown() from any other task to stop the server: it stops accepting, waits for in-flight requests to drain, runs the ASGI lifespan shutdown, and closes the listener, in that order.

Two async queues carry the handshake. request receives the shutdown trigger (a signal delivered through the runtime's global cancellation reaches the server the same way — see serve_graceful). done is posted once the server has finished the whole shutdown sequence, so shutdown() can block until the port is actually free.

request : @aqueue.Queue[Unit]
done : @aqueue.Queue[Unit]
fnfn ShutdownHandle::new() -> Self

Create an idle shutdown handle with empty unbounded signal queues.

fnasync fn ShutdownHandle::request_stop(Self) -> Unit

Request a graceful shutdown without waiting for it to finish.

fnasync fn ShutdownHandle::shutdown(Self) -> Unit

Request a graceful shutdown and block until the server has drained in-flight requests, run lifespan shutdown, and closed the listener. Returns once the listen port is free again.

structstruct Site

The serving process, as a listener beside the TCP one sees it.

fnfn Site::admit(Self) -> Bool

Take a new request, or refuse it past limit_concurrency. Every request taken is given back with finished, which is what shutdown waits for.

fnfn Site::bound(Self) -> @socket.Addr

The address the server listens on.

fnfn Site::config(Self) -> @config.Config

The configuration the server was started with, which is what the second listener needs to agree with the first about limits and timeouts.

fnasync fn Site::finished(Self) -> Unit

A request taken with admit is over.

fnfn Site::head(Self, Int32, Array[(String, String)]) -> Array[@header.Header]

A response head as a field section: :status, the application's fields that may cross, and the configured server fields.

fnfn Site::scope(Self, Array[@header.Header], version~ : String, peer~ : @socket.Addr, suite~ : Int32) -> @spec.Scope

The ASGI scope for a request head (RFC 9110 §7) that arrived from peer under TLS on HTTP version version, with suite naming its cipher.

fnfn Site::stopping(Self) -> Bool

Whether the server is shutting down, which is when a listener stops taking new work and lets what is running finish.

fnasync fn TlsCert::load(Self, alpn? : Array[String], suites? : Array[@record.Suite], suites12? : Array[@record.Suite12]) -> @tls.Server

Read the files into a TLS server configuration. A key that is not the leaf's is refused here, at startup, rather than by every client's handshake.

mooncat/cli10 items

The command line as a set of flags, and run(app) as a whole server program.

Values

fnfn command(version? : String) -> @argparse.Command

The command line, as argparse describes it.

Options carry no defaults on purpose: an absent flag must be distinguishable from one that happens to match the default, because everything not given is left as parse found it in base.

fnfn parse(ArrayView[String], base? : @config.Config) -> Plan raise

Read a command line into a Plan, starting from base and overriding only what was given.

--help and --version are argparse's: it prints and exits the process, as uvicorn and clap do, so they never come back here.

One wart worth stating: Logger is opaque, so --log-level and --access-log cannot adjust the one in base — either flag replaces it with a fresh logger at the level asked for, which drops a custom write. Neither flag leaves base's logger alone.

fnasync fn run(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, argv? : ArrayView[String], base? : @config.Config) -> Unit

Serve app the way the command line says to.

This is the whole of a server program:

async fn main {
@cli.run(app)
}
moon run src/main --target native -- --port 9000 --log-level debug

With no argv, the real one is read and its first element — the path to the executable — is dropped. base is where the flags start from, so a program that wants its own defaults passes them and lets the command line override only what was typed.

fnasync fn serve(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, Plan) -> Unit

Serve under a plan already parsed, for a program that wants to look at the plan — or change it — before the socket opens.

Errors

fnfn Refused::equal(Self, Self) -> Bool

From the type's derive(Eq).

fnfn Refused::not_equal(Self, Self) -> Bool

From the type's derive(Eq).

fnfn Refused::output(Self, &Logger) -> Unit

From the type's Show implementation.

fnfn Refused::to_repr(Self) -> @debug.Repr

From the type's derive(Debug).

fnfn Refused::to_string(Self) -> String

From the type's Show implementation.

implimpl Show for Refused

From the Show implementation.

mooncat/quic12 items

HTTP/3: the UDP socket, the datagram loop that routes by connection ID, and the request streams.

Values

fnfn now() -> Int64

The event loop's clock, in the microseconds the recovery and idle timers are measured in. @async.now() counts milliseconds since the epoch.

fnasync fn serve(String, @conn.Endpoint[@socket.Addr], tick_ms? : Int32) -> Unit

Bind a UDP socket at addr (e.g. "0.0.0.0:443") and serve server on it. The socket outlives the loop deliberately: closing one a task is parked in recvfrom on wedges it rather than waking it, so shutdown is cancellation of the task group, not a close.

fnasync fn serve_config(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @config.Config, @serve.TlsCert, suites? : Array[@record.Suite], suites12? : Array[@record.Suite12]) -> Unit

Serve HTTP/1.1, HTTP/2 and HTTP/3 together under one certificate, TCP and UDP on the same port: the TCP side tells clients about HTTP/3 with alt-svc (RFC 7838).

With workers above 1 the TCP port is shared by the kernel, as for serve_tls, and the UDP port by this process: it holds the socket and passes each datagram to the worker whose number is in the connection id (see route.mbt).

fnasync fn serve_endpoint(Endpoint, @conn.Endpoint[@socket.Addr], tick_ms? : Int32) -> Unit

Serve QUIC on ep until the enclosing task group is cancelled: one task reads datagrams and demultiplexes them onto connections, the other runs the idle, probe and closing-period timers every tick_ms milliseconds, and both push out whatever that left ready to send. A datagram that names no connection or fails to authenticate is dropped rather than killing the loop — an endpoint cannot tell a stray packet from an attack.

fnasync fn serve_site(async (@spec.Scope, async () -> @spec.Event, async (@spec.Event) -> Unit) -> Unit, @serve.Site, @tls.Server, transport? : @conn.Config, tick_ms? : Int32) -> Unit

Serve HTTP/3 beside the server's TCP listener, on UDP at the same address, until cancelled. tls offers h3.

Types and methods

structstruct Endpoint

A QUIC endpoint: where datagrams come from and go to.

fnasync fn Endpoint::bind(String) -> Self

Bind an endpoint to addr (e.g. "0.0.0.0:443", or "127.0.0.1:0" for an OS-assigned port). The bound address is available from local_addr.

fnfn Endpoint::close(Self) -> Unit

Close the endpoint's socket or link.

fnfn Endpoint::local_addr(Self) -> @socket.Addr

The address the endpoint serves (the concrete port when bound to port 0).

fnasync fn Endpoint::recv_datagram(Self, max? : Int32) -> (Bytes, @socket.Addr)

Receive the next datagram and its sender's address, copying up to max bytes.

fnasync fn Endpoint::relay(String, String, Int32, @socket.Addr) -> Self

A worker's endpoint: datagrams for serving arrive over the supervisor's link at relay, which this opens with token and the worker's number.

fnasync fn Endpoint::send_datagram(Self, Bytes, @socket.Addr) -> Unit

Send data as a single datagram to to.

102 public items across 7 packages.