mooncat — a native ASGI 3.0 server for MoonBit (← uvicorn), built on moonbitlang/async and the moonasgi SEAM. HTTP/1.1, HTTP/2, WebSocket and HTTP/3 over moonquic.
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, …)"]// An ASGI app: respond 200 with a text body.
let app : @moonasgi.AsgiApp = (_scope, _receive, send) => {
send(@moonasgi.Event::HttpResponseStart(
status=200, headers=[("content-type", "text/plain")]))
send(@moonasgi.Event::HttpResponseBody(
body=b"Hello from mooncat!", more_body=false))
}
// Serve it (inside an async context / task group):
@mooncat.serve(app, host="127.0.0.1", port=12000)@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 / 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)pub suberror Http3ConnError {
Http3ConnError(String)
}pub suberror Http3FrameError {
Http3FrameError(String)
}pub suberror LifespanError {
LifespanError(String)
}pub suberror QpackError {
QpackError(String)
}pub(all) struct Config {
host : String
port : Int
backlog : Int
dual_stack : Bool
reuse_addr : Bool
server_headers : Map[String, String]
max_connections : Int?
allow_failure : Bool
graceful_timeout : Int?
root_path : String
logger : Logger
timeout_keep_alive : Int
limit_max_requests : Int?
limit_concurrency : Int?
max_head_size : Int
date_header : Bool
proxy_headers : Bool
forwarded_allow_ips : Array[String]
ws : WsConfig
}fn Config::new(host? : String, port? : Int, backlog? : Int, dual_stack? : Bool, reuse_addr? : Bool, server_headers? : Map[String, String], max_connections? : Int, allow_failure? : Bool, graceful_timeout? : Int, root_path? : String, logger? : Logger, timeout_keep_alive? : Int, limit_max_requests? : Int, limit_concurrency? : Int, max_head_size? : Int, date_header? : Bool, proxy_headers? : Bool, forwarded_allow_ips? : Array[String], ws? : WsConfig) -> Configpub struct Http3Conn {
role : Http3Role
local_settings : Http3Settings
peer_settings : Http3Settings?
control_seen : Bool
}pub(all) struct Http3Request {
meth : String
scheme : String
authority : String
path : String
headers : Array[(String, String)]
body : Bytes
}fn Http3Settings::new(qpack_max_table_capacity : UInt64, max_field_section_size : UInt64, qpack_blocked_streams : UInt64) -> Http3Settingspub(all) enum Http3UniEvent {
ControlEstablished(Http3Settings)
QpackEncoder
QpackDecoder
Push(UInt64)
Reserved(UInt64)
} derive(Eq, Debug)pub(all) struct QpackDynamicTable {
entries : Array[(Bytes, Bytes)]
used : Int
capacity : Int
insert_count : Int
dropped : Int
}fn QpackDynamicTable::apply(self : QpackDynamicTable, inst : QpackEncoderInst) -> Bool raise QpackErrorpub(all) struct TlsCert {
certificate_file : String
private_key_file : String
pfx_file : String
}pub(all) struct WsConfig {
max_size : Int
}let h3_settings_max_field_section_size : UInt64let h3_settings_qpack_blocked_streams : UInt64let h3_settings_qpack_max_table_capacity : UInt64fn http3_request(meth : String, scheme : String, authority : String, path : String, headers : Array[(String, String)], body : Bytes) -> Bytesfn http3_serve(request_bytes : Bytes, handler : (Http3Request) -> Http3Response) -> Bytes raise Http3FrameErrorfn http_date(ms : Int64) -> Stringfn http_date_now() -> Stringlet port : Intfn qpack_decode_field_section_dyn(input : Bytes, table : QpackDynamicTable, max_entries : Int) -> Array[(String, String)] raise QpackErrorfn qpack_decode_required_insert_count(encoded : Int, max_entries : Int, total_inserts : Int) -> Int raise QpackErrorfn qpack_decode_section_prefix(input : BytesView, max_entries : Int, total_inserts : Int) -> (Int, Int, Int)? raise QpackErrorfn qpack_encode_dynamic(headers : Array[(String, String)], table : QpackDynamicTable, max_entries : Int, huffman? : Bool) -> (Bytes, Bytes)fn qpack_encode_section_prefix(req_insert_count : Int, base : Int, max_entries : Int) -> Bytesfn qpack_encode_string(data : Bytes, prefix_bits : Int, pattern : Int, huffman : Bool) -> Bytesfn qpack_entry_size(name : Bytes, value : Bytes) -> Intfn qpack_int_decode(input : BytesView, prefix_bits : Int) -> (Int, Int)?fn qpack_int_encode(value : Int, prefix_bits : Int, flags : Int) -> Bytesfn qpack_max_entries(max_capacity : Int) -> Intfn qpack_static_find(name : String, value : String) -> Int?fn qpack_static_find_name(name : String) -> Int?fn qpack_static_get(index : Int) -> (String, String)?fn qpack_static_len() -> Intfn quic_now() -> Int64async 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) -> Unitfn tls13_certificate_verify_check(key : PublicKey, context : String, transcript_hash : Bytes, body : Bytes) -> Boolfn tls13_certificate_verify_sign(key : PrivateKey, context : String, transcript_hash : Bytes) -> Bytesfn tls13_client_hello_x25519(client_hello : Bytes) -> Bytes?fn tls13_ecdhe_handshake_traffic_secret(private_key : Bytes, peer_public : Bytes, transcript_hash : Bytes, is_client : Bool) -> Bytesfn tls13_ecdhe_secrets_from_client_hello(client_hello : Bytes, server_private : Bytes, transcript_hash : Bytes) -> (Bytes, Bytes)?fn tls13_ecdhe_shared(private_key : Bytes, peer_public : Bytes) -> Bytesfn tls13_handshake_secret_from_ecdhe(private_key : Bytes, peer_public : Bytes) -> Bytesfn tls13_server_hs_secret_from_client_hello(client_hello : Bytes, server_private : Bytes, transcript_hash : Bytes) -> Bytes?fn tls13_x25519_public(private_key : Bytes) -> BytesInstall
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 and HTTP/3 over moonquic.