moonbitstack/moonzero/zrpc does not have a README file

    BidiStreamFactory

    type BidiStreamFactory = () -> BidiStreamHandler

    A factory that mints one BidiStreamHandler per call, so each stream gets its own handler state (← the fresh ServerStream gRPC hands every bidi invocation).

    ClientStreamHandler

    type ClientStreamHandler = (Array[Bytes]) -> Bytes

    A client-streaming handler: every request message the client sends is collected, and after the client half-closes the handler returns one reply.

    Interceptor

    type Interceptor = ((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise)

    One layer around an invoker.

    Invoker

    type Invoker = (Call) -> Result[Bytes,
    Status
    ] raise

    The innermost of a chain: makes the call.

    RpcHandler

    type RpcHandler = (Bytes) -> Bytes

    A unary RPC handler: it maps a request message's wire bytes to a response message's wire bytes (the application/grpc+proto payload, sans the length-prefix framing that @moonrpc.encode_message adds). Streaming handlers arrive with the h2 transport; this is the unary shape zRPC registers today.

    ServerStreamHandler

    type ServerStreamHandler = (Bytes) -> Array[Bytes]

    A server-streaming handler: one request message in, an ordered sequence of response messages out (each framed as its own length-prefixed gRPC message). Mirrors go-zero's pb.XxxServer server-streaming method, which writes to a grpc.ServerStream instead of returning one reply.

    Authenticator

    pub(open) trait Authenticator {
    fn authenticate(Self, Call) -> Unit raise
    }

    What a peer must prove before its call runs. The framework owns the seam and not the scheme: a token check over redis, a signed request, an mTLS peer identity — each is an implementation of this trait, so the interceptor never grows a branch per mechanism.

    BidiCall

    pub struct BidiCall {
    channel : RpcChannel
    sid : Int
    pending : Bytes
    status_code : Int
    ended : Bool
    }

    A live client-side bidirectional call over the h2c channel (← gRPC's ClientStream): the request stream stays open while messages flow both ways. send writes one request message and returns whatever replies the server produced right then (bidi interleaving — an echo handler answers each message as it arrives); close_send half-closes the request stream, runs the server's on_end, and reports the final grpc-status. The channel's HPACK decoder is advanced across every reply block, so its dynamic table stays in lockstep with the engine's encoder for the life of the call. pending holds DATA octets not yet split into a whole length-prefixed message (a message may straddle two DATA frames under flow control).

    BidiCall::close_send

    fn BidiCall::close_send(self : BidiCall) -> Result[Array[Bytes],
    Status
    ] raise

    Half-close the request stream: run the server's on_end, return its final reply messages, and map the grpc-status trailer to Ok/Err. Calling it a second time is an error (Cancelled).

    BidiCall::send

    fn BidiCall::send(self : BidiCall, msg : Bytes) -> Array[Bytes] raise

    Send one request message on the open stream and return the replies the server emitted in response to it (possibly empty). A no-op once the stream is half-closed.

    BidiStreamHandler

    pub(all) struct BidiStreamHandler {
    on_message : (Bytes) -> Array[Bytes]
    on_end : () -> Array[Bytes]
    }

    A live bidirectional call (← go-zero's pb.XxxServer bidi method, which reads from and writes to the same grpc.ServerStream): on_message fires once per request message and returns the replies to send right then, so responses interleave with requests; on_end runs after the client half-closes and returns the final replies before the grpc-status trailer. The moonzero-local mirror of @moonrpc.BidiHandler, so callers register bidi methods without naming the transport package.

    Call

    pub(all) struct Call {
    path : String
    request : Bytes
    parent :
    TraceContext
    ?
    }

    One unary call: its gRPC path (/package.Service/Method) and the request message's wire bytes.

    Call::name

    fn Call::name(self : Call) -> String

    The value the name of the method is not: method is reserved in MoonBit, and the path already carries the service and the name.

    ClientDeps

    pub(all) struct ClientDeps {
    clock :
    Clock

    tracer :
    Tracer

    breaker :
    Breaker

    metrics : RpcMetrics
    now : () -> Int64
    timeout_ms : Int64
    slow_ms : Int64
    mute : Array[String]
    }

    Everything the client chain needs that is not a flag: the seam values a framework injects rather than looks up.

    ClientDeps::new

    fn ClientDeps::new(clock? :
    Clock
    , tracer? :
    Tracer
    , breaker? :
    Breaker
    , metrics? : RpcMetrics, now? : () -> Int64, timeout_ms? : Int64, slow_ms? : Int64, mute? : Array[String]) -> ClientDeps raise
    Refused

    The dependencies for a client config, with every seam at its default: a system clock, a discarding tracer, a fresh breaker and a fresh metric registry.

    MethodTimeout

    pub(all) struct MethodTimeout {
    path : String
    budget_ms : Int64
    }

    A per-method deadline: path gets budget_ms instead of the service's.

    RpcChannel

    pub struct RpcChannel {
    engine :
    H2Server

    encoder :
    Encoder

    decoder :
    Decoder

    authority : String
    next_stream_id : Int
    interceptors : Array[((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise)]
    }

    An in-process gRPC channel bound to a server engine — the client half of the h2c transport. A call is carried as the real HTTP/2 frames a socket-backed client would send: an HPACK-coded HEADERS block with the gRPC pseudo-headers, a length-prefixed DATA frame closing the stream, and the grpc-status trailer read back off the engine's reply. The channel's HPACK encoder pairs with the engine's decoder and vice versa, so the dynamic-table state stays in lockstep across every call on the channel.

    RpcChannel::call

    fn RpcChannel::call(self : RpcChannel, path : String, request : Bytes) -> Result[Bytes,
    Status
    ] raise

    Invoke a unary method at path with request as its message payload, driving the call through the h2c engine and returning the reply payload on grpc-status: 0, or the mapped @moonrpc.Status otherwise. request and the returned reply are bare message bytes; the length-prefix framing is applied and stripped by the transport.

    RpcChannel::call_bidi_streaming

    fn RpcChannel::call_bidi_streaming(self : RpcChannel, path : String, requests : Array[Bytes]) -> Result[Array[Bytes],
    Status
    ] raise

    Drive a whole bidirectional call at path in one shot: send every message in requests (collecting the interleaved replies in order), then half-close and append the on_end replies. The result is every reply message the server produced, in emission order, or the non-zero grpc-status the stream closed with.

    RpcChannel::call_client_streaming

    fn RpcChannel::call_client_streaming(self : RpcChannel, path : String, requests : Array[Bytes]) -> Result[Bytes,
    Status
    ] raise

    Invoke a client-streaming method at path: send every message in requests as its own DATA frame, half-close the stream, and read back the single reply. An empty requests still opens and half-closes the stream, so the handler runs with no messages.

    RpcChannel::call_server_streaming

    fn RpcChannel::call_server_streaming(self : RpcChannel, path : String, request : Bytes) -> Result[Array[Bytes],
    Status
    ] raise

    Invoke a server-streaming method at path: send the single request message and read back the ordered sequence of reply messages the server produced, or the mapped error @moonrpc.Status if the stream closed with a non-zero grpc-status. On Ok the array holds every message in emission order (possibly empty).

    RpcChannel::call_through

    fn RpcChannel::call_through(self : RpcChannel, path : String, request : Bytes, parent? :
    TraceContext
    ) -> Result[Bytes,
    Status
    ]

    Invoke path through the client interceptor chain. The chain sees every failure the transport reports, including an unknown method, and can refuse a call before it reaches the wire.

    RpcChannel::connect

    fn RpcChannel::connect(server : RpcServer, authority? : String) -> RpcChannel

    Open a channel to server over an in-process h2c transport, exchanging the opening SETTINGS the way a real connection does. Client-initiated streams use odd identifiers (RFC 7540 §5.1.1), starting at 1.

    RpcChannel::interceptors

    fn RpcChannel::interceptors(self : RpcChannel) -> Array[((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise)]

    The interceptors this channel's unary calls run through, outermost first.

    RpcChannel::open_bidi

    fn RpcChannel::open_bidi(self : RpcChannel, path : String) -> BidiCall raise

    Open a bidirectional stream to path, sending the request HEADERS without half-closing so the stream stays open for interleaved sends. An unregistered path answers trailers-only UNIMPLEMENTED during this HEADERS feed, which the returned call captures as its status.

    RpcChannel::with_interceptors

    fn RpcChannel::with_interceptors(self : RpcChannel, interceptors : Array[((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise)]) -> RpcChannel

    A channel with interceptors as its client chain, outermost first.

    RpcGroup

    pub struct RpcGroup {
    server : RpcServer
    service : String
    }

    A per-service registration handle (← go-zero's service registrar closure): binds a set of methods to one package.Service on a shared RpcServer.

    RpcGroup::register

    fn RpcGroup::register(self : RpcGroup, name : String, handler : (Bytes) -> Bytes) -> Unit

    Register a method name on this group's service, building the @moonrpc.Method descriptor and installing handler under its gRPC path. (register, not method — the latter is a reserved word.)

    RpcGroup::register_bidi_streaming

    fn RpcGroup::register_bidi_streaming(self : RpcGroup, name : String, factory : () -> BidiStreamHandler) -> Unit

    Register a bidirectional-streaming method name on this group's service.

    RpcGroup::register_client_streaming

    fn RpcGroup::register_client_streaming(self : RpcGroup, name : String, handler : (Array[Bytes]) -> Bytes) -> Unit

    Register a client-streaming method name on this group's service.

    RpcGroup::register_server_streaming

    fn RpcGroup::register_server_streaming(self : RpcGroup, name : String, handler : (Bytes) -> Array[Bytes]) -> Unit

    Register a server-streaming method name on this group's service.

    RpcGroup::service

    fn RpcGroup::service(self : RpcGroup) -> String

    The fully-qualified package.Service this group registers under.

    RpcMetrics

    The per-method call metrics of one side of the wire.

    RpcMetrics::calls

    fn RpcMetrics::calls(self : RpcMetrics, path : String, status : Int) -> Double

    The number of calls recorded for path with status.

    RpcMetrics::new

    Metrics for the client side (kind="client") or the server side (kind="server").

    RpcMetrics::observations

    fn RpcMetrics::observations(self : RpcMetrics, path : String) -> Int64

    The latency observations recorded for path.

    RpcMetrics::observe

    fn RpcMetrics::observe(self : RpcMetrics, path : String, status : Int, duration_ms : Double) -> Unit

    Record one finished call: its duration in milliseconds and its status code, under the method's full path.

    RpcMetrics::registry

    The registry these families hang off.

    RpcMetrics::to_exposition

    fn RpcMetrics::to_exposition(self : RpcMetrics) -> String

    The two families as the Prometheus text exposition a /metrics scrape reads.

    RpcServer

    pub struct RpcServer {
    conf :
    RpcServerConf

    handlers : Map[String, (Bytes) -> Bytes]
    server_streaming : Map[String, (Bytes) -> Array[Bytes]]
    client_streaming : Map[String, (Array[Bytes]) -> Bytes]
    bidi_streaming : Map[String, () -> BidiStreamHandler]
    interceptors : Array[((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise)]
    }

    A zRPC server (← go-zero's zrpc.Server): config plus a registry mapping each method's gRPC :path (/package.Service/Method) to its handler. Handlers are registered via @moonrpc.Method descriptors — directly or through a RpcGroup — and dispatched by path, mirroring how go-zero registers service implementations on the underlying gRPC server. Unary, server-streaming, and client-streaming methods live in separate registries so one path resolves to exactly one cardinality.

    RpcServer::conf

    The server's configuration.

    RpcServer::dispatch

    fn RpcServer::dispatch(self : RpcServer, path : String, request : Bytes) -> Result[Bytes,
    Status
    ]

    Dispatch a unary call to the handler registered for path, returning the response bytes. An unregistered path yields Err(Unimplemented) — exactly the grpc-status a real gRPC server returns for an unknown method — so a transport can translate the result straight onto the wire.

    RpcServer::dispatch_graceful

    fn RpcServer::dispatch_graceful(self : RpcServer, coord :
    ShutdownCoordinator
    , path : String, request : Bytes) -> Result[Bytes,
    Status
    ]

    Dispatch a unary call through the shutdown gate: refuse with Unavailable when the server is shutting down, otherwise run the handler and count it as in-flight for the duration so a concurrent shutdown drains behind it. The gate wraps RpcServer::dispatch, so an unregistered path still yields Unimplemented.

    RpcServer::group

    fn RpcServer::group(self : RpcServer, service : String) -> RpcGroup

    Open a RpcGroup that registers methods under the fully-qualified package.Service name — go-zero's per-service registration, without repeating the service name on each method.

    RpcServer::has_method

    fn RpcServer::has_method(self : RpcServer, path : String) -> Bool

    Whether a handler is registered for path.

    RpcServer::interceptors

    fn RpcServer::interceptors(self : RpcServer) -> Array[((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise)]

    The interceptors this server's unary calls run through, outermost first.

    RpcServer::invoke

    Dispatch one unary call through the interceptor chain, then the registered handler. An unregistered path answers Unimplemented from the innermost invoker, so the chain still sees and records it.

    RpcServer::lookup

    fn RpcServer::lookup(self : RpcServer, path : String) -> (Bytes) -> Bytes?

    Look up the handler registered for a gRPC :path, or None if unregistered.

    RpcServer::methods

    fn RpcServer::methods(self : RpcServer) -> Array[String]

    The gRPC paths of every registered method.

    RpcServer::new

    Build an empty RPC server from its config.

    RpcServer::of

    Build a server from conf with the chain its Middlewares flags ask for.

    RpcServer::register

    fn RpcServer::register(self : RpcServer, desc :
    Method
    , handler : (Bytes) -> Bytes) -> Unit

    Register handler for method, keyed by its gRPC path. A later registration for the same path replaces the earlier one.

    RpcServer::register_bidi_streaming

    fn RpcServer::register_bidi_streaming(self : RpcServer, desc :
    Method
    , factory : () -> BidiStreamHandler) -> Unit

    Register a bidirectional-streaming handler for method, keyed by its gRPC path. factory runs once per call so each stream gets fresh handler state.

    RpcServer::register_client_streaming

    fn RpcServer::register_client_streaming(self : RpcServer, desc :
    Method
    , handler : (Array[Bytes]) -> Bytes) -> Unit

    Register a client-streaming handler for method, keyed by its gRPC path.

    RpcServer::register_server_streaming

    fn RpcServer::register_server_streaming(self : RpcServer, desc :
    Method
    , handler : (Bytes) -> Array[Bytes]) -> Unit

    Register a server-streaming handler for method, keyed by its gRPC path.

    RpcServer::to_h2

    Build a @moonrpc.H2Server protocol engine from this zRPC server's registered handlers — the transport-facing view of the same registry dispatch reads. Each handler is bound to its gRPC path, so a request arriving over the h2c transport is dispatched to exactly the handler the group registered.

    RpcServer::use_

    fn RpcServer::use_(self : RpcServer, interceptor : ((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise)) -> RpcServer

    A server with interceptor added at the front of the chain, so the layers are named from the outermost in.

    RpcServer::with_interceptors

    fn RpcServer::with_interceptors(self : RpcServer, interceptors : Array[((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise)]) -> RpcServer

    A server whose unary calls run through interceptors, outermost first.

    ServerDeps

    pub(all) struct ServerDeps {
    clock :
    Clock

    tracer :
    Tracer

    breaker :
    Breaker

    shedder :
    Shedder

    metrics : RpcMetrics
    authenticator : &Authenticator?
    now : () -> Int64
    timeout_ms : Int64
    stat : StatConf
    }

    Everything the server chain needs that is not a flag.

    ServerDeps::new

    The dependencies for a server config, with every seam at its default: a system clock, a discarding tracer, a fresh breaker and shedder, and no authenticator — a server that does not say who may call it accepts anybody, which is what Auth: false means.

    StatConf

    pub(all) struct StatConf {
    slow_ms : Int64
    ignore_content_methods : Array[String]
    }

    What the stat interceptor logs: the slow-call threshold, and the methods whose payload must not reach a log line.

    StatConf::new

    fn StatConf::new(slow_ms? : Int64, ignore_content_methods? : Array[String]) -> StatConf

    The default stat policy: half a second counts as slow, and every payload is logged.

    acceptable

    fn acceptable(status :
    Status
    ) -> Bool

    Whether a failure says something about the peer. A cancelled or expired call says nothing about the backend, so it does not push the breaker towards open — the same split the reference implementation draws.

    chain

    fn chain(interceptors : Array[((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise)], base : (Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise)

    Fold interceptors around base, first in the array outermost — the order ChainUnaryInterceptor gives them.

    client_breaker

    The breaker interceptor: a call the throttle sheds never reaches the transport, and answers Unavailable; a failure the peer caused is counted against it.

    client_duration

    fn client_duration(clock :
    Clock
    , logger? :
    Logger
    , slow_ms? : Int64, mute? : Array[String]) -> (((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise))

    The duration interceptor: a failed call is logged at error with how long it took, and a successful one slower than slow_ms is logged as a slow call. A method listed in mute is logged without its request body, the way a login or a payment call is.

    client_interceptors

    The client chain conf asks for, outermost first.

    client_prometheus

    fn client_prometheus(metrics : RpcMetrics, clock :
    Clock
    ) -> (((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise))

    The Prometheus interceptor: time the call and count it under its method and status code.

    client_timeout

    fn client_timeout(budget_ms : Int64, clock :
    Clock
    ) -> (((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise))

    The timeout interceptor: a call that ran past budget_ms is reported as DeadlineExceeded.

    The deadline is checked when the call returns rather than enforced against a timer: this transport runs the peer in the caller's own task, so there is no frame to abandon. A socket-backed client hands the budget to the transport, which does preempt; the decision this records is the same either way.

    client_tracing

    fn client_tracing(tracer :
    Tracer
    , now? : () -> Int64) -> (((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise))

    The tracing interceptor: open a client span for the call, run it as a child of whatever context the caller carried, and record the resulting status code on the span.

    default_latency_buckets

    let default_latency_buckets : Array[Double]

    The latency buckets the reference implementation registers for an RPC method.

    internal_status

    The grpc-status an error becomes when a layer cannot say anything better. The transport's own failures are internal: the caller asked for a method, not for a protocol exchange.

    server_auth

    fn server_auth(authenticator : &Authenticator) -> (((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise))

    The authentication interceptor: a peer that cannot prove itself is refused with Unauthenticated and its call never reaches the handler.

    It runs outermost, ahead of the breaker and the metric families, so a stream of unauthenticated calls cannot push the breaker open or fill the counters with traffic the service never served.

    server_breaker

    The server breaker interceptor: a shed call never reaches the handler and answers Unavailable. A deadline or a shed on the way in says nothing about this service, so it does not count as a failure.

    server_interceptors

    The server chain conf asks for, outermost first: authentication, tracing, recovery, the stat log, the metric families, the breaker, shedding and the deadline.

    server_prometheus

    fn server_prometheus(metrics : RpcMetrics, clock :
    Clock
    ) -> (((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise))

    The server Prometheus interceptor: time the call and count it under its method and status code.

    server_recover

    The recovery interceptor: an error raised anywhere below is reported as Internal instead of reaching the transport, and logged with its method so the reason is not lost. A handler itself cannot raise — the transport hands it a request and takes back bytes — but a layer added below can.

    server_shedding

    The shedding interceptor: a call is admitted against the adaptive shedder, and refused with ResourceExhausted when the machine is past its threshold. A refused call is reported to the shedder as a drop; an admitted one is reported by how it ended.

    server_stat

    The stat interceptor: every call is timed, and a slow one is logged with its method. A method in ignore_content_methods is logged without the payload size, so a credential never reaches a log file.

    server_timeout

    fn server_timeout(budget_ms : Int64, clock :
    Clock
    , methods? : Array[MethodTimeout]) -> (((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise))

    The timeout interceptor: a call that ran past its budget is reported as DeadlineExceeded. methods overrides the service budget for named paths.

    The deadline is checked when the handler returns, not raced against a timer: this transport runs the peer in the caller's task, so there is no frame to abandon. A socket-backed server hands the same budget to its read deadline, which does preempt.

    server_tracing

    fn server_tracing(tracer :
    Tracer
    , now? : () -> Int64) -> (((Call) -> Result[Bytes,
    Status
    ] raise) -> ((Call) -> Result[Bytes,
    Status
    ] raise))

    The server tracing interceptor: open a server span for the call, as a child of the context the client sent, and record the resulting status on it.

    status_code

    fn status_code(status :
    Status
    ) -> Int

    The numeric grpc-status of a status, the value that goes on the wire.

    status_name

    fn status_name(status :
    Status
    ) -> String

    The grpc-status name of a status, for a log line or a span attribute.

    status_of_code

    fn status_of_code(code : Int) ->
    Status

    Map a numeric grpc-status code back to a @moonrpc.Status. Anything outside the canonical 0–16 range is reported as Unknown, matching how a gRPC client treats an unrecognised code.