Real gRPC for MoonBit over HTTP/2: all four call kinds, metadata, deadlines, rich errors, health checking, server reflection, retries, hedging and load balancing, with pure client and server engines that run on every backend.
Dependencies
Moved on mooncakes from Lfan-ke/moonrpc to moonbitstack/moonrpc.
let server = @net.GrpcServer::new()
// unary: one request, one reply.
server.register("/greet.Greeter/SayHello", req => handle(req))
// server-streaming: one request, an ordered run of replies.
server.register_server_streaming("/count.C/Up", (ctx, req) => [
first(req), second(req), third(req),
])
// client-streaming: many requests collected, one reply at half-close.
server.register_client_streaming("/sum.S/Add", (ctx, msgs) => fold(msgs))
// bidi: each request answered as it arrives, a farewell at half-close.
server.register_bidi("/chat.C/Echo", ctx => @moonrpc.BidiHandler::{
on_message: m => [reply_to(m)],
on_end: () => [b"bye"],
})
server.serve() // listens on 12000| Package | What it holds | Depends on |
|---|---|---|
| moonrpc/wire | message framing, Status, Method, metadata rules, keepalive, HTTP/2 defaults | moonhttp, moonvar, moonbase |
| moonrpc/proto | PbWriter/PbReader, the descriptor model and its codec | moonvar |
| moonrpc/stream | the stream state machine, RpcContext, the four handler kinds | moonhttp |
| moonrpc/balance | retry, hedging, connectivity states, balancers, DNS, the channel pool | wire, moondate, moonpool, moonvar |
| moonrpc/client | H2Client, CallReply | wire, moonhttp, moonzip |
| moonrpc/server | H2Server, interceptors | wire, proto, stream, moonhttp, moonzip, moonbase |
| moonrpc/service | health checking and server reflection | server, stream, proto, moonvar |
| moonrpc/net | GrpcServer, Channel, MuxChannel, ManagedChannel over TCP (native) | the root, moonbitlang/async |
let chan = @net.Channel::connect("127.0.0.1", @net.port)
// unary.
let reply = chan.unary("/greet.Greeter/SayHello", request)
reply.messages[0] // the reply message; reply.grpc_status is 0 on success
// server-streaming: one request, every framed reply reassembled in order.
let out = chan.server_streaming("/count.C/Up", request) // out.messages
// client-streaming: many requests, one reply.
let sum = chan.client_streaming("/sum.S/Add", [a, b, c])
// a deadline: sent as grpc-timeout and enforced locally — if it elapses the
// stream is reset and grpc_status comes back DEADLINE_EXCEEDED (4).
let bounded = chan.unary("/slow.S/Wait", request, timeout_millis=200)let health = @moonrpc.HealthService::new()
health.set_status("greet.Greeter", Serving)
server.register_health(health) // Check + Watch on /grpc.health.v1.Health/*
engine.add_unary_interceptor((ctx, req, next) => {
// inspect, then proceed — or return without calling next to short-circuit.
next(ctx, req)
})let w = @moonrpc.PbWriter::new()
w.string_(1, "testing") // field 1, a length-delimited string
w.int32(2, -1) // field 2, a varint (negative -> ten octets)
let r = @moonrpc.PbReader::new(w.to_bytes())
let (field, wire) = r.read_tag() // (1, LengthDelim)
r.read_string() // "testing"let greeter : @moonrpc.ServiceDescriptor = {
name: "Greeter",
methods: [
{ name: "SayHello", input_type: ".greet.HelloRequest",
output_type: ".greet.HelloReply",
client_streaming: false, server_streaming: false },
],
}
let refl = @moonrpc.ReflectionService::new()
refl.add_file(@moonrpc.FileDescriptor::new("greet.proto", "greet", services=[greeter]))
server.register_reflection(refl)let engine = @moonrpc.H2Server::new()
engine.register("/echo.Echo/Say", req => req)
let out = engine.feed(frame) // -> [HEADERS(:status 200), DATA(reply), HEADERS(grpc-status:0)]let frame = @moonrpc.encode_message(b"hello grpc")
// [0x00][0x00 0x00 0x00 0x0A]["hello grpc"] — 1-byte flag + 4-byte big-endian length + payload
let (compressed, payload) = @moonrpc.decode_message(frame).unwrap()
// (false, b"hello grpc")
@moonrpc.Status::code(NotFound) // 5
@moonrpc.Status::name(Unauthenticated) // "UNAUTHENTICATED", one of the 17 codes
let m : @moonrpc.Method = { service: "greet.Greeter", name: "SayHello" }
m.path() // "/greet.Greeter/SayHello"fn decode_health_request(msg : Bytes) -> Bytesfn decode_message(data : Bytes) -> (Bool, Bytes)?let default_max_frame_size : Intlet default_max_streams : Intlet default_window_size : Intfn dns_encode_query(id : Int, name : String, qtype : Int) -> Bytesfn encode_grpc_timeout(millis : Int) -> Bytesfn encode_health_request(service : Bytes) -> Bytesfn encode_message(payload : Bytes, compressed? : Bool) -> Bytesfn is_binary_metadata(name : Bytes) -> Boollet max_header_list_size : Intlet max_message_size : Intfn message_len(data : BytesView, at? : Int) -> Int?fn metadata_value_from_wire(name : Bytes, value : Bytes) -> Bytesfn metadata_value_to_wire(name : Bytes, value : Bytes) -> Bytesfn parse_grpc_timeout(v : Bytes) -> Int?fn percent_decode(v : Bytes) -> Stringlet reflection_v1_path : Stringlet reflection_v1alpha_path : Stringfn stream_id_valid_for_initiator(id : Int, by_client~ : Bool) -> Boolfn stream_is_client_initiated(id : Int) -> Boolfn stream_is_server_initiated(id : Int) -> BoolInstall
Download zipReal gRPC for MoonBit over HTTP/2: all four call kinds, metadata, deadlines, rich errors, health checking, server reflection, retries, hedging and load balancing, with pure client and server engines that run on every backend.
Dependencies