mooncat — a native ASGI 3.0 server for MoonBit (← uvicorn), built on moonbitlang/async and the moonasgi SEAM. HTTP/1.1, HTTP/2, WebSocket, TLS and HTTP/3, a uvicorn-shaped command line, and a process model with graceful shutdown; the protocols themselves are moonhttp, moontls and moonquic.
Dependencies
Moved on mooncakes from Lfan-ke/mooncat to moonbitstack/mooncat.
flowchart LR
net["moonbitlang/async<br/>(sockets · TLS · HTTP/1.1)"] --> cat["**mooncat**<br/>accept loop"]
cat -->|"Scope / Receive / Send"| asgi(["moonasgi SEAM"])
asgi --> app["your app<br/>(moonapi, …)"]async fn main {
let app : @moonasgi.AsgiApp = (_scope, _receive, send) => {
send(
@moonasgi.Event::HttpResponseStart(
status=200,
headers=[("content-type", "text/plain; charset=utf-8")],
trailers=false,
),
)
send(
@moonasgi.Event::HttpResponseBody(body=b"Hello from mooncat!", more_body=false),
)
}
@mooncat.serve(app, host="127.0.0.1", port=12000) catch {
err => println("mooncat stopped: \{err}")
}
}moon run src/main --target native
curl http://127.0.0.1:12000async fn main {
@cli.run(app) catch {
err => println("mooncat: \{err}")
}
}moon run src/main --target native -- --port 9000 --log-level debug
moon run src/main --target native -- --help$ moon run src/main --target native -- --workers 4
mooncat: --workers: mooncat serves from one process; there is no fork here// All three are required arguments; `serve_tls` reads the pair its platform
// accepts and ignores the other, so one call compiles everywhere.
@mooncat.serve_tls(
app,
certificate_file="certs/cert.pem", // PEM cert, OpenSSL platforms
private_key_file="certs/key.pem", // PEM key, OpenSSL platforms
pfx_file="certs/dev.pfx", // PKCS#12, Windows and SChannel
port=12000,
)let handle = @mooncat.ShutdownHandle::new()
g.spawn(() => @mooncat.serve_graceful(app, @mooncat.Config::new(port=12000), handle~))
// … later, from anywhere:
handle.shutdown() // stop accepting → drain in-flight → lifespan shutdown → close@mooncat.serve_h2c(app, host="127.0.0.1", port=12000)| Package | What | What it costs to link |
|---|---|---|
| mooncat | Re-exports the four below, so @mooncat.serve works as before | all four |
| mooncat/config | Config, access and error logging, the HTTP date | moonlog, moondate |
| mooncat/lifespan | the ASGI lifespan protocol | moonasgi |
| mooncat/proxy | forwarded headers from a trusted proxy | — |
| mooncat/serve | The accept loop, workers and reload, HTTP/1.1, h2c, WebSocket, TLS | moonasgi, moonhttp's http1 / http2 / hpack / ws / upgrade, moonbitlang/async |
| mooncat/cli | uvicorn's flags, and run(app) | the above, plus core's argparse |
| mooncat/quic | QUIC and HTTP/3: the UDP socket, the datagram loop, the handshake QUIC does inline | moonquic, moontls, mooncred's certificate reader, moonhttp/http3 |
fn http_date(ms : Int64) -> Stringfn http_date_now() -> Stringlet port : Intasync fn reload_watch(path : String, on_reload : async () -> Unit) -> Unitasync fn serve_graceful(app : async (Scope, async () -> Event, async (Event) -> Unit) -> Unit, config : Config, handle? : ShutdownHandle) -> UnitInstall
Download zipmooncat — a native ASGI 3.0 server for MoonBit (← uvicorn), built on moonbitlang/async and the moonasgi SEAM. HTTP/1.1, HTTP/2, WebSocket, TLS and HTTP/3, a uvicorn-shaped command line, and a process model with graceful shutdown; the protocols themselves are moonhttp, moontls and moonquic.
Dependencies