moonrpc

    moonrpc — a real gRPC implementation for MoonBit (← gRPC). v0.8.1 adds Channel::stream_take, which drives a streaming call inline — no background reader — and returns once it has collected a bounded number of reply messages, so a caller can consume the first N of an unbounded subscription (etcd's Watch, a long server-streaming feed) and close, keeping the whole exchange in one task. v0.8.0 completes the client channel stack. Load balancing picks a READY sub-connection per call (pick_first or round_robin) from a connection pool that tracks each address through the gRPC connectivity states (IDLE/CONNECTING/READY/TRANSIENT_FAILURE/SHUTDOWN); a ManagedChannel dials several backends over real sockets and routes calls through the picker. Name resolution self-builds a DNS message codec (RFC 1035, A/AAAA with name compression) and a UDP resolver, so dns_connect turns a hostname into the backends it dials. Retry and hedging policies drive re-issue and parallel attempts — hedging fires a fresh attempt every hedging delay and commits on the first fatal status, cancelling the rest. Client keepalive pings an idle connection and closes it on a missed ACK. A multiplexed channel (MuxChannel) carries many concurrent calls over one connection through a shared read pump with per-stream inboxes and serialized writes, drives keepalive, and supports interactive bidirectional streaming — send while receiving, replies read message by message. v0.7.0 adds per-message gzip: a self-built DEFLATE (RFC 1951, stored/fixed/dynamic-Huffman) and gzip (RFC 1952, CRC-32 + ISIZE) reader that decompresses gzip requests on the server and gzip responses on the client, and client-side retry with exponential backoff bounded by the call deadline. It also hardens every decoder against malformed input: the gRPC length prefix, HPACK integer/string, protobuf, and health decoders now reject an out-of-range or overflowing length instead of aborting; flow-control windows saturate rather than wrap; peer SETTINGS are validated; the client reassembles a response header block across CONTINUATION; and header blocks and message sizes are capped. v0.6.1 self-builds a pure protobuf wire runtime (PbWriter/PbReader for varint, zigzag, fixed32/64, and length-delimited fields) and Server Reflection: a descriptor model and the grpc.reflection.v1.ServerReflection service (with its v1alpha alias) answering ListServices, FileContainingSymbol, and FileByFilename. v0.6 added the client half — a real Channel, a long-lived multiplexed h2c connection over @socket.Tcp driven by a pure H2Client engine symmetric to the server — plus the grpc.health.v1.Health service, server interceptors, -bin metadata, google.rpc.Status rich errors, and client-side grpc-timeout enforcement. The server engine (H2Server) serves all four call kinds over the self-built HTTP/2 (h2c) transport, driving the RFC 7540 frame layer, the stream state machine, complete HPACK (RFC 7541, with Huffman + dynamic table), and connection- and stream-level flow control. Includes the shared length-prefixed framing and the 17-code gRPC status model.

    grpc
    rpc
    protobuf
    http2
    moonbit
    Download zip
    Version
    0.10.1
    License
    Apache-2.0
    Last updated
    2 hours ago
    Downloads
    16

    Dependencies

    #moonrpc

    A real gRPC implementation for MoonBit — ← gRPC.

    Check and Test License mooncakes

    Moved on mooncakes from Lfan-ke/moonrpc to moonbitstack/moonrpc.

    moonrpc targets real gRPC — not gRPC-Web. Where the MoonBit ecosystem lacks the primitives, we build them: the north star is a self-built HTTP/2 (RFC 7540) framing layer with stream multiplexing and HPACK (RFC 7541), carrying application/grpc+proto over h2c.

    v0.5 serves all four gRPC call kinds — unary, server-, client-, and bidirectional-streaming — over the self-built HTTP/2 (h2c) transport. A pure, all-backend protocol engine (H2Server) drives the frame layer, the stream state machine, complete HPACK, and connection- and stream-level flow control honoured across the whole multi-message exchange; a native moonbitlang/async socket driver (GrpcServer) pumps the bytes:

    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 echoed 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(port=50051) // a real gRPC / in-process client gets the replies

    The handler sees the call's RpcContext: the request metadata, the grpc-timeout deadline in milliseconds, and slots for response initial and trailing metadata.

    #The Channel client

    A Channel is one long-lived h2c connection; every call multiplexes over it on its own client-allocated stream id, sharing the connection's HPACK and flow-control state.

    let chan = @net.Channel::connect("127.0.0.1", 50051)

    // 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=Some(200))

    Under the Channel, H2Client is the same kind of pure, transport-independent engine as the server: it produces the request frames and consumes the response frames, so the whole client path is exercised in-memory on every backend against H2Server, and only the socket driver is native.

    #Health and interceptors

    The standard grpc.health.v1.Health service registers in one call, and server interceptors wrap unary and server-streaming handlers:

    let health = @moonrpc.HealthService::new()
    health.set_status("greet.Greeter", @moonrpc.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)
    })

    #Protobuf and Server Reflection

    A pure protobuf wire runtime carries the proto3 binary format — varint, zigzag, fixed32/fixed64, and length-delimited fields — as a PbWriter/PbReader pair that every message serialises through on every backend:

    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"

    On top of it, a descriptor model builds and parses the FileDescriptorProto / FileDescriptorSet fields the reflection path uses — package, message names and fields, services and methods — and the standard grpc.reflection.v1.ServerReflection service answers a reflection client over the same h2c transport — ListServices to enumerate, FileContainingSymbol / FileByFilename to describe:

    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) // a reflection client now lists + describes greet.Greeter

    Under the hood, everything below serve is a pure, transport-independent enginefeed turns a stream of decoded frames into the frames to write back, so the whole server path (HPACK, flow control, dispatch, HEADERS + DATA + grpc-status trailers) is exercised in-memory on every backend; only the socket driver is native.

    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)]

    It builds on the load-bearing transport primitives, all verified across every backend:

    #The gRPC message framing

    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")

    This is the Length-Prefixed-Message framing shared by gRPC-Web (over HTTP/1.1) and real gRPC (over HTTP/2), so the transport can be swapped underneath it without touching the codec.

    #The HTTP/2 frame layer (RFC 7540)

    All ten frame types encode to and decode from their exact wire bytes — a byte-level, exhaustively round-trippable codec that the multiplexer will layer on top without touching:

    let f = @moonrpc.Frame::Headers(
    stream_id=1, fragment=block, end_stream=true, end_headers=true,
    priority=None, padding=0,
    )
    let bytes = f.encode() // 9-octet header + payload
    let (frame, consumed) = @moonrpc.decode_frame(bytes) // raises Incomplete until whole

    @moonrpc.has_connection_preface(buf) // PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n (§3.5)

    The stream state machine drives the §5.1 lifecycle (idle / open / half-closed / closed) off END_STREAM / push / reset, and rejects illegal transitions:

    let s = @moonrpc.Stream::new(1)
    s.send(@moonrpc.StreamEvent::Headers(end_stream=false)) // -> Open
    s.recv(@moonrpc.StreamEvent::Data(end_stream=true)) // -> HalfClosedRemote

    #Complete HPACK (RFC 7541)

    The full header-compression stack: the 61-entry static table, prefix integers, Huffman coding (Appendix B), and the size-bounded dynamic table with eviction — driven by a stateful encoder/decoder pair over the six field representations. Verified byte-for-byte against the Appendix C worked examples.

    let enc = @moonrpc.HpackEncoder::new(huffman=true)
    let block = enc.encode([{ name: b":method", value: b"GET" },
    { name: b":authority", value: b"www.example.com" }])

    let dec = @moonrpc.HpackDecoder::new()
    let headers = dec.decode(block) // back to the same header list

    @moonrpc.huffman_encode(b"www.example.com") // f1e3 c2e5 f23a 6ba0 ab90 f4ff — §C.4.1

    #The status model

    @moonrpc.Status::code(NotFound) // 5
    @moonrpc.Status::name(Unauthenticated) // "UNAUTHENTICATED" (all 17 canonical grpc-status codes)

    let m : @moonrpc.Method = { service: "greet.Greeter", name: "SayHello" }
    m.path() // "/greet.Greeter/SayHello"

    #Roadmap (the self-built stack, sequenced to completeness)

    v0 = framing + status + method descriptors; v0.2 = the first HPACK primitives (static table + integer representation + non-Huffman string literals). v0.3 self-builds the load-bearing transport primitives: the HTTP/2 frame layer (all ten types — DATA / HEADERS / PRIORITY / RST_STREAM / SETTINGS / PUSH_PROMISE / PING / GOAWAY / WINDOW_UPDATE / CONTINUATION — with flags and payloads), the connection preface, the stream state machine (§5.1 + §5.1.1 id rules), and complete HPACK (Huffman coding + the dynamic table with eviction + the stateful encoder/decoder).

    v0.4 makes the server actually run: the H2Server engine reads frames off a connection, demultiplexes by stream id, drives the per-stream state machine, exchanges SETTINGS, and honours connection- and stream-level flow-control windows with WINDOW_UPDATE; it receives a request stream, dispatches to a registered (Bytes) -> Bytes handler, and responds with HEADERS (:status 200, grpc-encoding) + DATA + trailer HEADERS (grpc-status). The native GrpcServer binds this engine to real moonbitlang/async TCP sockets, proven by an in-process h2c client that makes a unary call end-to-end. Note: the async TLS layer exposes no ALPN, so h2 runs via h2c (prior-knowledge) until an ALPN hook lands upstream.

    v0.5 adds the streaming modes on the same engine. A method is registered as one of four cardinalities — unary, server-streaming ((ctx, req) -> [reply]), client-streaming ((ctx, [req]) -> reply), or bidi (a BidiHandler whose on_message fires per request message and whose on_end fires at half-close). The engine reassembles length-prefixed messages out of the DATA stream, routes each to the handler, and frames every produced message as its own length-prefixed DATA — flow control is honoured across the whole multi-message exchange, so a reply larger than the window splits and resumes on WINDOW_UPDATE. Request metadata and the grpc-timeout deadline are parsed and surfaced to the handler, which can set response initial and trailing metadata. Two in-process h2c clients — a server-streaming call receiving multiple framed replies in order and a client-streaming call sending many messages for one reply — prove it end-to-end, mutation-verified against the stream terminator and the flow-control windows.

    v0.6 adds the client half and the gRPC cross-cutting services. A real Channel — a long-lived, multiplexed h2c connection over a real @socket.Tcp, driven by a pure H2Client engine symmetric to the server — opens streams (client-allocated odd ids), HPACK-encodes request HEADERS, frames request DATA under the send windows, and reassembles the reply. It performs unary, server- and client-streaming calls against a real GrpcServer over an actual socket. The grpc.health.v1.Health service ships with a hand-coded protobuf codec for its two messages; Check is fully served, and Watch emits the current status once at subscribe time (a stream that also pushes on every later change needs the async engine variant). Server-side interceptors wrap unary and server-streaming handlers in an outermost-first chain that can rewrite the request, post-process the reply, or short-circuit. The grpc-timeout deadline is now enforced client-side: the Channel races the read loop against the timer and, when it elapses, resets the stream and surfaces DEADLINE_EXCEEDED.

    v0.6.1 self-builds the protobuf wire runtime and Server Reflection. A pure PbWriter/PbReader pair encodes and decodes the proto3 binary format (varint, zigzag, fixed32/fixed64, length-delimited, tag packing, unknown-field skipping, and truncation/overflow/group-type rejection). A descriptor model builds and parses the FileDescriptorProto / FileDescriptorSet subset the reflection path needs — package, message names and fields, services and methods — enough for a self-contained proto (enums, nested types, imports, and options are not yet modelled, so a decoded protoc set re-encodes only that subset), and the grpc.reflection.v1.ServerReflection service (with its v1alpha alias) answers ListServices, FileContainingSymbol, and FileByFilename as a bidi stream, returning the real descriptor bytes. An in-process reflection client, over an actual socket, lists and describes a registered service end-to-end, and the varint/descriptor path is mutation-verified.

    Delivered since: grpcurl interop in CI, -bin metadata round-trip, rich errors (google.rpc.Status), per-message gzip decompression on both sides, and client-side retry with exponential backoff.

    Also delivered on the server: what the engine now refuses. A request whose request line is not gRPC's — :method other than POST, a content-type that does not begin application/grpc, a missing te: trailers — is answered with an HTTP status (415 / 405 / 400) instead of the 200 a plain HTTP client would read as success. SETTINGS_MAX_CONCURRENT_STREAMS is advertised in the server preface and enforced on arrival, with REFUSED_STREAM for the stream over the limit. Received DATA is charged against the connection window before anything can refuse the frame, and overrunning either window is a FLOW_CONTROL_ERROR. A field block must arrive contiguously (§6.10). And the grpc-timeout deadline is enforced server-side against a clock the driver installs: a call already out of time never reaches its handler, one whose handler outran it never sends the reply, and H2Server::tick closes out a call that expired while it waited. Enforcement is not preemptive — a synchronous handler already running is not interrupted — which is what the concurrent read-demux engine below is for.

    Next: gzip response compression (the encode direction), channelz, connection keepalive and pooling, DNS resolution with connectivity-aware load-balancing, and full descriptor fidelity — enums, nested types, imports — with transitive-dependency reflection. The concurrent read-demux engine variant, which underpins interactive bidi, push-based health Watch, and preemptive server-side deadlines, is tracked as one piece.

    #License

    Apache-2.0.

    StreamInterceptor

    type StreamInterceptor = (RpcContext, Bytes, (RpcContext, Bytes) -> Array[Bytes]) -> Array[Bytes]

    A server-streaming interceptor: (ctx, request, next) -> replies. It can pre-process the request, post-process the reply sequence, or short-circuit.

    UnaryInterceptor

    type UnaryInterceptor = (RpcContext, Bytes, (RpcContext, Bytes) -> Bytes) -> Bytes

    A unary server interceptor: (ctx, request, next) -> reply, where next is the remainder of the chain. Call next(ctx, request) to proceed, or return without calling it to short-circuit.

    FrameError

    pub suberror FrameError {
    Incomplete
    FrameSizeError(String)
    ProtocolError(String)
    } derive(Eq)

    A raised decode failure. Incomplete is the normal "need more bytes" signal a streaming reader catches to wait for the rest; the others are protocol errors.
    impl Show for FrameError

    GzError

    pub suberror GzError {
    GzError(String)
    }

    A gzip / DEFLATE decode failure: a malformed member, an unsupported feature, or a checksum mismatch.

    HpackError

    pub suberror HpackError {
    HuffmanError(String)
    HpackDecodeError(String)
    } derive(Eq)

    A raised HPACK failure (Huffman decoding or header-block decoding).
    impl Show for HpackError

    PbError

    pub suberror PbError {
    Truncated
    BadWireType(Int)
    Overflow
    BadUtf8
    } derive(Eq)

    A raised protobuf decode failure: Truncated when the buffer ends inside a field, BadWireType for a group or unknown wire type, Overflow for a varint longer than ten octets, and BadUtf8 for an invalid string field.
    impl Show for PbError

    StreamError

    pub suberror StreamError {
    InvalidTransition(String)
    } derive(Eq)

    An illegal stream transition (RFC 7540 §5.1): a frame not permitted in the current state (typically a STREAM_CLOSED or PROTOCOL_ERROR condition).
    impl Show for StreamError

    BidiHandler

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

    A live bidirectional call. on_message is invoked once per fully-received request message and returns the reply messages to send right then; on_end runs after the client half-closes and returns the final replies. Both feed the same flow-controlled response stream, so responses interleave with requests.

    CallReply

    pub(all) struct CallReply {
    status : Bytes
    grpc_status : Int
    grpc_message : String
    headers : Array[Header]
    messages : Array[Bytes]
    trailers : Array[Header]
    }

    The completed result of a call: the HTTP :status, the numeric grpc-status (-1 if the peer never sent one), the response initial metadata, the reply messages in order, and the trailing metadata.

    ClientCall

    pub(all) struct ClientCall {
    id : Int
    path : Bytes
    authority : Bytes
    metadata : Array[Header]
    timeout : Bytes?
    out : Bytes
    out_off : Int
    req_ended : Bool
    req_headers_sent : Bool
    req_fin_sent : Bool
    send_window : Int
    data :
    Buffer

    data_off : Int
    messages : Array[Bytes]
    recv_window : Int
    status : Bytes
    grpc_status : Int?
    grpc_message : String
    resp_encoding : Bytes
    resp_headers : Array[Header]
    resp_trailers : Array[Header]
    headers_seen : Bool
    header_block :
    Buffer

    in_headers : Bool
    pending_end_stream : Bool
    decode_failed : Bool
    retryable : Bool
    done : Bool
    }

    One client-side call: its stream id, the request side (the length-prefixed request body still to send, the send window, and whether the request has been half-closed), and the response side (the accumulating DATA with a cursor over the messages already pulled out, the recv window, the captured :status and grpc-status, and the response initial and trailing metadata).

    ConnectionPool

    pub struct ConnectionPool {
    order : Array[String]
    subconns : Map[String, SubConn]
    policy : LbPolicy
    cursor : Int
    }

    A pool of sub-connections, one per resolved address (in resolver order), that picks a READY one per an LbPolicy.

    ConnectionPool::new

    fn ConnectionPool::new(addresses : Array[String], policy? : LbPolicy) -> ConnectionPool

    A pool over addresses (each a host:port), each sub-connection starting IDLE, choosing among the READY ones with policy (default PickFirst).

    ConnectionPool::pick

    fn ConnectionPool::pick(self : ConnectionPool) -> String?

    Pick a READY sub-connection's address for the next call, applying the LB policy over just the ready ones (← gRPC picker). None when no sub-connection is ready.

    ConnectionPool::ready_addresses

    fn ConnectionPool::ready_addresses(self : ConnectionPool) -> Array[String]

    The addresses of every READY sub-connection, in resolver order.

    ConnectionPool::subconn

    fn ConnectionPool::subconn(self : ConnectionPool, address : String) -> SubConn?

    The sub-connection for address, if the pool has one — the handle the Channel drives.

    ConnectivityState

    pub(all) enum ConnectivityState {
    Idle
    Connecting
    Ready
    TransientFailure
    Shutdown
    } derive(Eq,
    Debug
    )

    A sub-connection's connectivity state (← gRPC connectivity.State).

    DnsRecord

    pub(all) struct DnsRecord {
    name : String
    rtype : Int
    ttl : Int
    address : String
    } derive(Eq,
    Debug
    )

    One answer resource record, carrying the textual address for A/AAAA records (empty for other types).

    DnsResponse

    pub(all) struct DnsResponse {
    id : Int
    rcode : Int
    answers : Array[DnsRecord]
    } derive(Eq,
    Debug
    )

    A decoded DNS response: the transaction id echoed back, the response code (0 = NOERROR), and the answer records.

    DnsResponse::addresses

    fn DnsResponse::addresses(self : DnsResponse) -> Array[String]

    Every resolved A/AAAA address in the response, in record order.

    DynamicTable

    pub(all) struct DynamicTable {
    entries : Array[(Bytes, Bytes)]
    size : Int
    max_size : Int
    }

    The HPACK dynamic table: a FIFO of recently seen (name, value) entries, newest first (entries[0]), bounded by max_size octets where each entry costs name.len + value.len + 32 (RFC 7541 §4.1). Adding evicts the oldest entries until the newcomer fits; an entry larger than max_size empties the table and is not stored (§4.4).

    DynamicTable::add

    fn DynamicTable::add(self : DynamicTable, name : Bytes, value : Bytes) -> Unit

    Insert (name, value) at the front, evicting oldest entries to make room. If the entry alone exceeds max_size the table ends up empty (RFC 7541 §4.4).

    DynamicTable::count

    fn DynamicTable::count(self : DynamicTable) -> Int

    The number of entries currently in the dynamic table.

    DynamicTable::current_size

    fn DynamicTable::current_size(self : DynamicTable) -> Int

    The current total size of the dynamic table in octets (§4.1 accounting).

    DynamicTable::new

    fn DynamicTable::new(max_size? : Int) -> DynamicTable

    A new empty dynamic table bounded by max_size octets (default 4096, the HTTP/2 initial SETTINGS_HEADER_TABLE_SIZE).

    DynamicTable::set_max_size

    fn DynamicTable::set_max_size(self : DynamicTable, new_max : Int) -> Unit

    Resize the table (a dynamic table size update, RFC 7541 §4.2), evicting to fit.

    FieldDescriptor

    pub(all) struct FieldDescriptor {
    name : String
    number : Int
    label : FieldLabel
    type_ : FieldType
    type_name : String
    } derive(Eq)

    One field of a message (FieldDescriptorProto). type_name is the fully-qualified name of the referenced message or enum for TypeMessage / TypeEnum, and empty for scalars.

    FieldDescriptor::decode

    fn FieldDescriptor::decode(body : Bytes) -> FieldDescriptor raise PbError

    Decode a FieldDescriptorProto.

    FieldDescriptor::encode

    fn FieldDescriptor::encode(self : FieldDescriptor) -> Bytes

    Encode a FieldDescriptorProto.

    FieldDescriptor::scalar

    fn FieldDescriptor::scalar(name : String, number : Int, type_ : FieldType, label? : FieldLabel) -> FieldDescriptor

    A field with a scalar type and no composite type_name.

    FieldLabel

    pub(all) enum FieldLabel {
    LabelOptional
    LabelRequired
    LabelRepeated
    } derive(Eq)

    The FieldDescriptorProto.Label: proto3 fields are LabelOptional unless repeated.

    FieldLabel::code

    fn FieldLabel::code(self : FieldLabel) -> Int

    The descriptor.proto enum number of a label.

    FieldLabel::from_code

    fn FieldLabel::from_code(n : Int) -> FieldLabel

    The label for a descriptor.proto enum number; anything else reads as LabelOptional.

    FieldType

    pub(all) enum FieldType {
    TypeDouble
    TypeFloat
    TypeInt64
    TypeUint64
    TypeInt32
    TypeFixed64
    TypeFixed32
    TypeBool
    TypeString
    TypeMessage
    TypeBytes
    TypeUint32
    TypeEnum
    TypeSfixed32
    TypeSfixed64
    TypeSint32
    TypeSint64
    } derive(Eq)

    The FieldDescriptorProto.Type enum (proto3's scalar and composite field types). TypeGroup is intentionally absent — groups are removed from proto3.

    FieldType::code

    fn FieldType::code(self : FieldType) -> Int

    The descriptor.proto enum number of a field type.

    FieldType::from_code

    fn FieldType::from_code(n : Int) -> FieldType

    The field type for a descriptor.proto enum number; an unrecognised number (including the removed group type 10) reads as TypeMessage.

    FileDescriptor

    pub(all) struct FileDescriptor {
    name : String
    package_ : String
    messages : Array[MessageDescriptor]
    services : Array[ServiceDescriptor]
    syntax : String
    } derive(Eq)

    A single .proto file (FileDescriptorProto): its filename, package, the message and service types it defines, and the syntax level. This is the unit Server Reflection returns.

    FileDescriptor::decode

    fn FileDescriptor::decode(body : Bytes) -> FileDescriptor raise PbError

    Decode a FileDescriptorProto.

    FileDescriptor::encode

    fn FileDescriptor::encode(self : FileDescriptor) -> Bytes

    Encode a FileDescriptorProto.

    FileDescriptor::new

    fn FileDescriptor::new(name : String, package_ : String, messages? : Array[MessageDescriptor], services? : Array[ServiceDescriptor]) -> FileDescriptor

    A proto3 file with the given filename and package.

    FileDescriptor::service_names

    fn FileDescriptor::service_names(self : FileDescriptor) -> Array[String]

    The fully-qualified names of the services this file defines (package.Service).

    FileDescriptor::symbols

    fn FileDescriptor::symbols(self : FileDescriptor) -> Array[String]

    The fully-qualified names this file defines: package.Service for each service and package.Message for each message. These are the symbols a FileContainingSymbol reflection request can resolve to this file.

    Frame

    pub(all) enum Frame {
    Data(stream_id~ : Int, data~ : Bytes, end_stream~ : Bool, padding~ : Int)
    Headers(stream_id~ : Int, fragment~ : Bytes, end_stream~ : Bool, end_headers~ : Bool, priority~ : Priority?, padding~ : Int)
    Priority(stream_id~ : Int, priority~ : Priority)
    RstStream(stream_id~ : Int, error_code~ : Int)
    Settings(params~ : Array[(Int, Int)], ack~ : Bool)
    PushPromise(stream_id~ : Int, promised_id~ : Int, fragment~ : Bytes, end_headers~ : Bool, padding~ : Int)
    Ping(payload~ : Bytes, ack~ : Bool)
    GoAway(last_stream_id~ : Int, error_code~ : Int, debug~ : Bytes)
    WindowUpdate(stream_id~ : Int, increment~ : Int)
    Continuation(stream_id~ : Int, fragment~ : Bytes, end_headers~ : Bool)
    Unknown(ftype~ : Int, flags~ : Int, stream_id~ : Int, payload~ : Bytes)
    } derive(Eq)

    A decoded HTTP/2 frame (RFC 7540 §6). Each variant carries the semantic payload with padding already stripped; padding is the number of padding bytes to (re)emit. Unknown preserves extension/unrecognised frames verbatim so a reader can forward or ignore them (RFC 7540 §4.1).

    Frame::encode

    fn Frame::encode(self : Frame) -> Bytes

    Encode this frame to its complete wire representation (9-octet header + payload), the exact inverse of decode_frame.

    Frame::frame_type

    fn Frame::frame_type(self : Frame) -> Int

    The numeric frame-type code of this frame (RFC 7540 §6).

    FrameHeader

    pub(all) struct FrameHeader {
    length : Int
    ftype : Int
    flags : Int
    stream_id : Int
    } derive(Eq)

    The fixed 9-octet frame header (RFC 7540 §4.1): a 24-bit payload length, an 8-bit type, 8-bit flags, a reserved bit, and a 31-bit stream identifier.

    FrameHeader::encode

    fn FrameHeader::encode(self : FrameHeader) -> Bytes

    Encode a FrameHeader to its 9 wire octets.

    H2Client

    pub struct H2Client {
    encoder : HpackEncoder
    decoder : HpackDecoder
    calls : Map[Int, ClientCall]
    next_id : Int
    conn_send_window : Int
    conn_recv_window : Int
    remote_initial_window : Int
    remote_max_frame : Int
    goaway_received : Bool
    goaway_last_stream_id : Int
    orphan_blocks : Map[Int,
    Buffer
    ]
    }

    The client side of one HTTP/2 connection: the HPACK codec pair (stateful across every call on the connection), the live calls keyed by stream id, the next odd stream id to allocate (§5.1.1), and the connection-level flow-control state bounded by the peer's SETTINGS.

    H2Client::close_send

    fn H2Client::close_send(self : H2Client, id : Int) -> Array[Frame]

    Half-close the request side of a call (no more request messages) and return any frames that completes — the trailing END_STREAM.

    H2Client::feed

    fn H2Client::feed(self : H2Client, frame : Frame) -> Array[Frame] raise

    Feed one decoded response frame to the engine, advancing all state and returning the frames to write back (SETTINGS ack, PING pong, WINDOW_UPDATE replenishing a receive window, and — once a WINDOW_UPDATE lifts back-pressure — any remaining request DATA). Captures :status, grpc-status, response metadata, and the reassembled reply messages.

    H2Client::goaway_last_stream_id

    fn H2Client::goaway_last_stream_id(self : H2Client) -> Int

    The peer's last processed stream id from its GOAWAY (0 if none was seen); a call whose stream id is above it was never handled by the server.

    H2Client::goaway_received

    fn H2Client::goaway_received(self : H2Client) -> Bool

    Whether the peer has sent GOAWAY; a draining connection opens no new stream, so the driver picks a fresh one for further calls. Mirrors H2Server::goaway_received.

    H2Client::has_messages

    fn H2Client::has_messages(self : H2Client, id : Int) -> Bool

    Whether a call has reply messages buffered but not yet taken — for reading a streaming response incrementally as it arrives, rather than all at once via reply.

    H2Client::is_done

    fn H2Client::is_done(self : H2Client, id : Int) -> Bool

    Whether a call has fully completed (its response ended). An unknown id counts as done so a driver loop terminates.

    H2Client::is_retryable

    fn H2Client::is_retryable(self : H2Client, id : Int) -> Bool

    Whether call id was left retryable by a GOAWAY: its stream id is above the peer's last processed id, so the server never saw it and re-issuing the RPC on a fresh connection is safe (gRPC transport GOAWAY / RFC 7540 §6.8). False for a call at or below that id — it may have been processed — and when no GOAWAY arrived.

    H2Client::new

    fn H2Client::new() -> H2Client

    A fresh client engine with no live calls. The first call takes stream id 1.

    H2Client::open

    fn H2Client::open(self : H2Client, path : String, metadata? : Array[Header], authority? : String, timeout_millis? : Int?) -> Int

    Open a new call on this connection for path (/pkg.Service/Method), returning its freshly allocated stream id. metadata is sent as custom request HEADERS; timeout_millis, when set, becomes the grpc-timeout header. The request body is added with send and half-closed with close_send.

    H2Client::preface

    fn H2Client::preface(self : H2Client) -> Array[Frame]

    The client's opening frames (RFC 7540 §3.5): a SETTINGS frame disabling server push. Written right after the 24-octet connection preface bytes, before any request.

    H2Client::release

    fn H2Client::release(self : H2Client, id : Int) -> Unit

    Drop everything the engine still holds for a finished call: its buffered body, messages, header block and trailers. The map is only ever inserted into otherwise, so a long-lived connection would keep one full request and response per RPC it has ever made, and every WINDOW_UPDATE and SETTINGS frame would walk that whole history. An unknown or already-released id is a no-op.

    H2Client::reply

    fn H2Client::reply(self : H2Client, id : Int) -> CallReply

    The completed result of a call. Meaningful once is_done is true.

    H2Client::send

    fn H2Client::send(self : H2Client, id : Int, message : Bytes, end? : Bool) -> Array[Frame]

    Append one request message to a call and return the frames to write now (the request HEADERS the first time, then as much length-prefixed DATA as the send windows allow). Set end on the last message to half-close the request.

    H2Client::take_messages

    fn H2Client::take_messages(self : H2Client, id : Int) -> Array[Bytes]

    Take every reply message received so far, clearing the call's buffer — the incremental counterpart to reply, for a server- or bidi-streaming response read message by message.

    H2Client::unary

    fn H2Client::unary(self : H2Client, path : String, request : Bytes, metadata? : Array[Header], authority? : String, timeout_millis? : Int?) -> (Int, Array[Frame])

    Open a unary call and return (stream_id, frames_to_write) in one step: the request HEADERS and the single length-prefixed request message with END_STREAM.

    H2Server

    pub struct H2Server {
    handlers : Map[String, Handler]
    encoder : HpackEncoder
    decoder : HpackDecoder
    streams : Map[Int, SrvStream]
    unary_interceptors : Array[(RpcContext, Bytes, (RpcContext, Bytes) -> Bytes) -> Bytes]
    stream_interceptors : Array[(RpcContext, Bytes, (RpcContext, Bytes) -> Array[Bytes]) -> Array[Bytes]]
    conn_recv_window : Int
    conn_send_window : Int
    remote_initial_window : Int
    remote_max_frame : Int
    max_streams : Int
    clock : () -> Int64
    continuing : Int?
    goaway_received : Bool
    last_client_stream : Int
    closing : Bool
    drained : Int?
    }

    The server side of one HTTP/2 connection: the HPACK codec pair, the live streams, the connection-level flow-control windows, and the peer's settings that bound what we may send. Persistent across the whole connection because HPACK and flow control are stateful.

    H2Server::active_streams

    fn H2Server::active_streams(self : H2Server) -> Int

    How many streams are still running: the ones RFC 9113 §5.1.2 counts against SETTINGS_MAX_CONCURRENT_STREAMS, which is open and both half-closed states. A graceful shutdown waits for this to reach zero before closing the connection. Finished streams stay in the map with their state but are not counted, and neither is one the engine only knows of because a WINDOW_UPDATE named it — that stream was never opened, so it holds nothing and occupies no slot.

    H2Server::add_stream_interceptor

    fn H2Server::add_stream_interceptor(self : H2Server, interceptor : (RpcContext, Bytes, (RpcContext, Bytes) -> Array[Bytes]) -> Array[Bytes]) -> Unit

    Add a server-streaming interceptor to the server's chain.

    H2Server::add_unary_interceptor

    fn H2Server::add_unary_interceptor(self : H2Server, interceptor : (RpcContext, Bytes, (RpcContext, Bytes) -> Bytes) -> Bytes) -> Unit

    Add a unary interceptor to the server's chain. Applies to every unary method; interceptors run in registration order, outermost first.

    H2Server::closing

    fn H2Server::closing(self : H2Server) -> Bool

    Whether a connection error has been reported. The GOAWAY carrying it came back from the feed that found it, so the driver writes that and then closes; the engine processes nothing more (RFC 9113 §5.4.1).

    H2Server::feed

    fn H2Server::feed(self : H2Server, frame : Frame) -> Array[Frame]

    Feed one decoded incoming frame to the engine, advancing all state and returning the frames to write back (SETTINGS ack, PING pong, WINDOW_UPDATE, and — as the request stream progresses — the framed gRPC response messages and trailers). A protocol fault comes back as the frames that report it rather than as a raise the driver can only discard: RST_STREAM for a stream error, and for a connection error a GOAWAY, after which closing is set and every later frame is ignored (RFC 9113 §5.4).

    H2Server::goaway

    fn H2Server::goaway(self : H2Server, last_stream_id : Int) -> Array[Frame]

    Frames for a graceful shutdown: a GOAWAY announcing last_stream_id — the highest client stream the server will still process (RFC 7540 §6.8) — with NO_ERROR, so the peer opens no new streams while in-flight ones finish. The driver writes these before closing the connection. A stream opened above last_stream_id afterwards is declined with RST_STREAM(REFUSED_STREAM), which tells the client it was never processed and can be re-issued elsewhere.

    H2Server::goaway_received

    fn H2Server::goaway_received(self : H2Server) -> Bool

    Whether the peer has sent GOAWAY; the driver stops accepting new streams once this is set.

    H2Server::highest_stream

    fn H2Server::highest_stream(self : H2Server) -> Int

    The highest client stream id this connection has opened, which is what a GOAWAY has to name so the peer knows which of its streams were accepted.

    H2Server::new

    fn H2Server::new() -> H2Server

    A fresh server engine with no registered handlers. Flow-control windows start at the HTTP/2 defaults until the peer's SETTINGS adjust them.

    H2Server::preface

    fn H2Server::preface(self : H2Server) -> Array[Frame]

    The server's opening frames (RFC 7540 §3.5): a SETTINGS frame disabling server push and naming how many streams this connection will run at once. Sent immediately after the client connection preface is validated, before any request frame is read.

    H2Server::register

    fn H2Server::register(self : H2Server, path : String, handler : (Bytes) -> Bytes) -> Unit

    Register a unary handler for a fully-qualified gRPC path (/pkg.Service/Method): one request message in, one reply message out. An unmatched path gets a trailers-only grpc-status: 12 (UNIMPLEMENTED) response.

    H2Server::register_bidi

    fn H2Server::register_bidi(self : H2Server, path : String, factory : (RpcContext) -> BidiHandler) -> Unit

    Register a bidirectional-streaming handler. The factory runs once per call and returns a BidiHandler whose on_message fires per request message (its replies stream out immediately) and whose on_end fires at half-close.

    H2Server::register_client_streaming

    fn H2Server::register_client_streaming(self : H2Server, path : String, handler : (RpcContext, Array[Bytes]) -> Bytes) -> Unit

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

    H2Server::register_handler

    fn H2Server::register_handler(self : H2Server, path : String, handler : Handler) -> Unit

    Register a handler of any of the four gRPC call kinds.

    H2Server::register_server_streaming

    fn H2Server::register_server_streaming(self : H2Server, path : String, handler : (RpcContext, Bytes) -> Array[Bytes]) -> Unit

    Register a server-streaming handler: one request message, an ordered sequence of reply messages, each framed as its own gRPC message.

    H2Server::register_unary

    fn H2Server::register_unary(self : H2Server, path : String, handler : (RpcContext, Bytes) -> Bytes) -> Unit

    Register a unary handler that also sees the call context (metadata, deadline, and the response metadata slots).

    H2Server::release

    fn H2Server::release(self : H2Server, id : Int) -> Unit

    Drop everything the engine still holds for a finished stream. The map is only ever inserted into otherwise, so a long-lived connection would keep one full request and response per RPC it has ever served. An unknown id is a no-op.

    H2Server::set_clock

    fn H2Server::set_clock(self : H2Server, clock : () -> Int64) -> Unit

    Install the clock the engine measures grpc-timeout deadlines against: a function returning the current time in milliseconds on any monotonic scale. The engine is a pure state machine with no clock of its own, so until one is installed every deadline is inert; the native driver installs @async.now.

    H2Server::set_max_streams

    fn H2Server::set_max_streams(self : H2Server, n : Int) -> Unit

    Set how many streams this connection may run at once. The value is both advertised as SETTINGS_MAX_CONCURRENT_STREAMS and enforced on arrival, so set it before preface and the peer is told exactly what it will be held to.

    H2Server::stream_state

    fn H2Server::stream_state(self : H2Server, id : Int) -> StreamState

    The lifecycle state of stream id, or Idle if the engine has never seen it.

    H2Server::tick

    fn H2Server::tick(self : H2Server) -> Array[Frame]

    Expire every call whose deadline has passed, returning the frames that end them. The driver calls this on whatever schedule it keeps time on; the engine also checks the deadline whenever a stream advances, so a call that is making progress never needs a tick to be cut off.

    Handler

    pub(all) enum Handler {
    Unary((RpcContext, Bytes) -> Bytes)
    ServerStreaming((RpcContext, Bytes) -> Array[Bytes])
    ClientStreaming((RpcContext, Array[Bytes]) -> Bytes)
    Bidi((RpcContext) -> BidiHandler)
    }

    A registered method, in one of gRPC's four cardinalities. The reply shape mirrors the request shape: streaming handlers produce an ordered Array[Bytes] of messages, each framed as its own length-prefixed gRPC message on the wire.
    pub(all) struct Header {
    name : Bytes
    value : Bytes
    } derive(Eq,
    Debug
    )

    A decoded header field: name and value as raw octet strings (HTTP/2 header names and values are byte sequences, and gRPC -bin metadata is binary).

    HealthService

    pub struct HealthService {
    statuses : Map[String, ServingStatus]
    }

    A grpc.health.v1.Health service backed by a per-service status table. The empty key "" is the overall-server status; a fresh service reports the whole server SERVING.

    HealthService::check

    fn HealthService::check(self : HealthService, service : String) -> ServingStatus

    The serving status of a named service: its set status, or ServiceUnknown when the service was never registered.

    HealthService::handlers

    fn HealthService::handlers(self : HealthService) -> Array[(String, Handler)]

    The (path, handler) pairs implementing the service: Check as a unary method and Watch as a server-streaming method that emits the current status. A live Watch that also pushes on every later change needs a streaming source the pure engine's eager ServerStreaming shape does not model, so this emits the status at subscribe time — the first message a real Watch always sends.

    HealthService::install

    fn HealthService::install(self : HealthService, server : H2Server) -> Unit

    Register Check and Watch on a pure server engine.

    HealthService::new

    A health service reporting the overall server as SERVING.

    HealthService::set_status

    fn HealthService::set_status(self : HealthService, service : String, status : ServingStatus) -> Unit

    Set the serving status of a named service (or the overall server with "").

    HedgingPolicy

    pub(all) struct HedgingPolicy {
    max_attempts : Int
    hedging_delay_millis : Int
    non_fatal : Array[Int]
    }

    A method's hedging policy. max_attempts counts every parallel attempt (gRPC caps it at 5); a new attempt originates every hedging_delay_millis while attempts remain. A finished attempt commits the whole call unless its status is one of non_fatal, in which case hedging continues.

    HedgingPolicy::attempt_cap

    fn HedgingPolicy::attempt_cap(self : HedgingPolicy) -> Int

    The effective attempt cap: the configured max_attempts, but never above gRPC's ceiling of 5.

    HedgingPolicy::can_start_another

    fn HedgingPolicy::can_start_another(self : HedgingPolicy, attempts_started : Int) -> Bool

    Whether another hedged attempt may originate given attempts_started (the number already launched): only while attempts remain under the cap.

    HedgingPolicy::default

    fn HedgingPolicy::default() -> HedgingPolicy

    A conventional default: up to 3 parallel attempts, a fresh one every 500 ms, treating only UNAVAILABLE as non-fatal — so a transient transport failure keeps hedging while a real application error commits at once.

    HedgingPolicy::delay_millis

    fn HedgingPolicy::delay_millis(self : HedgingPolicy) -> Int

    The delay between originating hedged attempts. The async driver waits this long before launching the next parallel attempt; 0 fires them all at once.

    HedgingPolicy::should_commit

    fn HedgingPolicy::should_commit(self : HedgingPolicy, status_code : Int) -> Bool

    Whether a finished attempt with status_code commits the whole call — stopping hedging and returning this result. OK commits (success), and so does any error not listed as non-fatal (a real failure retrying won't fix). A non-fatal error does not commit; hedging continues.

    HpackDecoder

    pub(all) struct HpackDecoder {
    table : DynamicTable
    limit : Int
    }

    A stateful HPACK decoder: it owns a dynamic table that persists across the header blocks of a connection. limit is the peer-agreed hard cap (SETTINGS_HEADER_TABLE_SIZE) a size update may not exceed.

    HpackDecoder::decode

    fn HpackDecoder::decode(self : HpackDecoder, block : Bytes) -> Array[Header] raise HpackError

    Decode one complete header block into its header list (RFC 7541 §6), mutating the dynamic table for incrementally indexed fields and size updates. Raises HpackDecodeError/HuffmanError on any malformed representation.

    HpackDecoder::new

    fn HpackDecoder::new(max_size? : Int) -> HpackDecoder

    A new decoder whose dynamic table is bounded by max_size octets (also the hard cap enforced on dynamic table size updates).

    HpackEncoder

    pub(all) struct HpackEncoder {
    table : DynamicTable
    huffman : Bool
    }

    A stateful HPACK encoder: it owns a dynamic table mirroring the decoder's, and prefers indexed representations. huffman selects Huffman string literals when they are shorter.

    HpackEncoder::encode

    fn HpackEncoder::encode(self : HpackEncoder, headers : Array[Header]) -> Bytes

    Encode a header list into a header block (RFC 7541 §6), using indexed fields where possible and literal-with-incremental-indexing otherwise (mutating the dynamic table to mirror what the peer decoder will build). The output decodes back to the same header list via HpackDecoder.

    HpackEncoder::new

    fn HpackEncoder::new(max_size? : Int, huffman? : Bool) -> HpackEncoder

    A new encoder bounded by max_size octets; huffman (default true) enables the shorter-of-two string-literal heuristic.

    Keepalive

    pub struct Keepalive {
    keepalive_time : Int64
    keepalive_timeout : Int64
    last_activity : Int64
    ping_outstanding : Bool
    ping_sent_at : Int64
    }

    Keepalive state for one connection: the idle threshold that triggers a PING, the ACK deadline that declares the connection dead, and the timers tracking the last activity and any in-flight PING.

    Keepalive::is_timed_out

    fn Keepalive::is_timed_out(self : Keepalive, now : Int64) -> Bool

    Whether the connection is dead at now: a PING is outstanding and its ACK has not arrived within keepalive_timeout of sending it.

    Keepalive::new

    fn Keepalive::new(keepalive_time~ : Int64, keepalive_timeout~ : Int64, now? : Int64) -> Keepalive

    A keepalive that pings after keepalive_time of idleness and gives a PING keepalive_timeout to be acknowledged. now seeds the activity clock.

    Keepalive::on_activity

    fn Keepalive::on_activity(self : Keepalive, now : Int64) -> Unit

    Record connection activity (a frame sent or received) at now, resetting the idle timer.

    Keepalive::on_ping_ack

    fn Keepalive::on_ping_ack(self : Keepalive, now : Int64) -> Unit

    Record that the PING's ACK arrived at now — the connection is alive, and this counts as activity.

    Keepalive::on_ping_sent

    fn Keepalive::on_ping_sent(self : Keepalive, now : Int64) -> Unit

    Record that a keepalive PING was sent at now.

    Keepalive::should_ping

    fn Keepalive::should_ping(self : Keepalive, now : Int64) -> Bool

    Whether a keepalive PING should be sent at now: the connection has been idle at least keepalive_time and no PING is already awaiting its ACK.

    LbPolicy

    pub(all) enum LbPolicy {
    PickFirst
    RoundRobin
    } derive(Eq,
    Debug
    )

    A client-side load-balancing policy (← gRPC loadBalancingConfig).

    LoadBalancer

    pub struct LoadBalancer {
    addresses : Array[String]
    policy : LbPolicy
    cursor : Int
    }

    A load balancer over a resolver's addresses, applying an LbPolicy to choose the next one.

    LoadBalancer::new

    fn LoadBalancer::new(addresses : Array[String], policy? : LbPolicy) -> LoadBalancer

    A balancer over addresses (each a host:port) using policy (default PickFirst).

    LoadBalancer::pick

    fn LoadBalancer::pick(self : LoadBalancer) -> String?

    The address the next call should use, or None when the resolver produced none. PickFirst always returns the first address; RoundRobin returns each in turn, wrapping around.

    LoadBalancer::size

    fn LoadBalancer::size(self : LoadBalancer) -> Int

    The number of addresses the balancer is choosing among.

    MessageDescriptor

    pub(all) struct MessageDescriptor {
    name : String
    fields : Array[FieldDescriptor]
    } derive(Eq)

    A message type (DescriptorProto): its (simple) name and its fields.

    MessageDescriptor::decode

    fn MessageDescriptor::decode(body : Bytes) -> MessageDescriptor raise PbError

    Decode a DescriptorProto.

    MessageDescriptor::encode

    fn MessageDescriptor::encode(self : MessageDescriptor) -> Bytes

    Encode a DescriptorProto.

    Method

    pub(all) struct Method {
    service : String
    name : String
    }

    A fully-qualified RPC method: package.Service and the method name.

    Method::path

    fn Method::path(self : Method) -> String

    The gRPC HTTP/2 :path, i.e. /package.Service/Method.

    MethodDescriptor

    pub(all) struct MethodDescriptor {
    name : String
    input_type : String
    output_type : String
    client_streaming : Bool
    server_streaming : Bool
    } derive(Eq)

    One RPC method (MethodDescriptorProto): its name, the fully-qualified request and response message names, and the two streaming flags that together pick the call cardinality.

    MethodDescriptor::decode

    fn MethodDescriptor::decode(body : Bytes) -> MethodDescriptor raise PbError

    Decode a MethodDescriptorProto.

    MethodDescriptor::encode

    fn MethodDescriptor::encode(self : MethodDescriptor) -> Bytes

    Encode a MethodDescriptorProto.

    PbReader

    pub struct PbReader {
    data : Bytes
    pos : Int
    }

    A forward cursor over an encoded protobuf message. read_tag pulls the next field's number and wire type; the typed readers then consume its body. skip discards an unknown field's body so a decoder tolerates fields it does not know (protobuf forward compatibility).

    PbReader::eof

    fn PbReader::eof(self : PbReader) -> Bool

    Whether the whole message has been consumed.

    PbReader::new

    fn PbReader::new(data : Bytes) -> PbReader

    A reader positioned at the start of data.

    PbReader::read_bool

    fn PbReader::read_bool(self : PbReader) -> Bool raise PbError

    Read a bool field body.

    PbReader::read_bytes

    fn PbReader::read_bytes(self : PbReader) -> Bytes raise PbError

    Read a bytes field body.

    PbReader::read_fixed32

    fn PbReader::read_fixed32(self : PbReader) -> UInt raise PbError

    Read a little-endian 32-bit fixed value.

    PbReader::read_fixed64

    fn PbReader::read_fixed64(self : PbReader) -> UInt64 raise PbError

    Read a little-endian 64-bit fixed value.

    PbReader::read_int32

    fn PbReader::read_int32(self : PbReader) -> Int raise PbError

    Read an int32 field body (the low 32 bits of the varint).

    PbReader::read_int64

    fn PbReader::read_int64(self : PbReader) -> Int64 raise PbError

    Read an int64 field body.

    PbReader::read_len_delim

    fn PbReader::read_len_delim(self : PbReader) -> Bytes raise PbError

    Read a length-delimited body's raw bytes.

    PbReader::read_map_string_string

    fn PbReader::read_map_string_string(self : PbReader) -> (String, String) raise PbError

    Read one map<string, string> entry (a length-delimited { key = 1, value = 2 } submessage) as (key, value). Call once the tag for the map field has been read.

    PbReader::read_packed_int32

    fn PbReader::read_packed_int32(self : PbReader) -> Array[Int] raise PbError

    Read a packed repeated int32 field: the length-delimited body decoded as back-to-back int32 varints.

    PbReader::read_sint32

    fn PbReader::read_sint32(self : PbReader) -> Int raise PbError

    Read a sint32 field body (zigzag-decoded).

    PbReader::read_sint64

    fn PbReader::read_sint64(self : PbReader) -> Int64 raise PbError

    Read a sint64 field body (zigzag-decoded).

    PbReader::read_string

    fn PbReader::read_string(self : PbReader) -> String raise PbError

    Read a string field body, decoding UTF-8. Raises BadUtf8 on invalid bytes.

    PbReader::read_tag

    fn PbReader::read_tag(self : PbReader) -> (Int, WireType) raise PbError

    Read a field tag, returning (field_number, wire_type). Raises BadWireType for a group or unknown wire type.

    PbReader::read_uint32

    fn PbReader::read_uint32(self : PbReader) -> UInt raise PbError

    Read a uint32 field body.

    PbReader::read_uint64

    fn PbReader::read_uint64(self : PbReader) -> UInt64 raise PbError

    Read a uint64 field body.

    PbReader::read_varint

    fn PbReader::read_varint(self : PbReader) -> UInt64 raise PbError

    Read a base-128 varint. Raises Overflow past ten octets and Truncated if the buffer ends mid-varint.

    PbReader::skip

    fn PbReader::skip(self : PbReader, wire : WireType) -> Unit raise PbError

    Discard the body of a field whose number the decoder does not recognise, given its wire type — the mechanism behind protobuf's forward compatibility.

    PbWriter

    pub struct PbWriter {
    buf :
    Buffer

    }

    An append-only protobuf message encoder. Field writers append a tag and the field body; to_bytes yields the finished message. Fields are written in the caller's order — protobuf places no ordering requirement on distinct fields, and a message struct's encoder writes them by ascending number by convention.

    PbWriter::bool_

    fn PbWriter::bool_(self : PbWriter, field : Int, v : Bool) -> Unit

    Write a bool field.

    PbWriter::bytes_

    fn PbWriter::bytes_(self : PbWriter, field : Int, v : Bytes) -> Unit

    Write a bytes field.

    PbWriter::enum_

    fn PbWriter::enum_(self : PbWriter, field : Int, v : Int) -> Unit

    Write an enum field (its integer value, as a varint).

    PbWriter::fixed32

    fn PbWriter::fixed32(self : PbWriter, field : Int, v : UInt) -> Unit

    Write a fixed32/sfixed32/float field.

    PbWriter::fixed64

    fn PbWriter::fixed64(self : PbWriter, field : Int, v : UInt64) -> Unit

    Write a fixed64/sfixed64/double field.

    PbWriter::int32

    fn PbWriter::int32(self : PbWriter, field : Int, v : Int) -> Unit

    Write an int32 field. Negative values sign-extend to a full ten-octet varint, exactly as the reference implementation encodes them.

    PbWriter::int64

    fn PbWriter::int64(self : PbWriter, field : Int, v : Int64) -> Unit

    Write an int64 field.

    PbWriter::map_string_string

    fn PbWriter::map_string_string(self : PbWriter, field : Int, key : String, value : String) -> Unit

    Write one map<string, string> entry for field: a length-delimited submessage { key = 1, value = 2 }, emitted once per pair (a proto map is repeated entries).

    PbWriter::message_

    fn PbWriter::message_(self : PbWriter, field : Int, v : Bytes) -> Unit

    Write an embedded-message field: the pre-encoded sub-message as a length-delimited body.

    PbWriter::new

    fn PbWriter::new() -> PbWriter

    A fresh, empty message encoder.

    PbWriter::packed_int32

    fn PbWriter::packed_int32(self : PbWriter, field : Int, values : Array[Int]) -> Unit

    Write a packed repeated int32 field: all values as back-to-back varints inside one length-delimited field (proto3's default for scalar repeated).

    PbWriter::sint32

    fn PbWriter::sint32(self : PbWriter, field : Int, v : Int) -> Unit

    Write a sint32 field (zigzag-encoded so small-magnitude negatives stay short).

    PbWriter::sint64

    fn PbWriter::sint64(self : PbWriter, field : Int, v : Int64) -> Unit

    Write a sint64 field (zigzag-encoded).

    PbWriter::string_

    fn PbWriter::string_(self : PbWriter, field : Int, v : String) -> Unit

    Write a string field (UTF-8 encoded).

    PbWriter::to_bytes

    fn PbWriter::to_bytes(self : PbWriter) -> Bytes

    The bytes written so far.

    PbWriter::uint32

    fn PbWriter::uint32(self : PbWriter, field : Int, v : UInt) -> Unit

    Write a uint32 field.

    PbWriter::uint64

    fn PbWriter::uint64(self : PbWriter, field : Int, v : UInt64) -> Unit

    Write a uint64 field.

    PbWriter::write_fixed32

    fn PbWriter::write_fixed32(self : PbWriter, v : UInt) -> Unit

    Append a little-endian 32-bit fixed value (wire type 5, no tag).

    PbWriter::write_fixed64

    fn PbWriter::write_fixed64(self : PbWriter, v : UInt64) -> Unit

    Append a little-endian 64-bit fixed value (wire type 1, no tag).

    PbWriter::write_len_delim

    fn PbWriter::write_len_delim(self : PbWriter, body : Bytes) -> Unit

    Append a length-delimited body: a varint length then the raw bytes (wire type 2, no tag).

    PbWriter::write_tag

    fn PbWriter::write_tag(self : PbWriter, field : Int, wire : WireType) -> Unit

    Append a field tag: (field_number << 3) | wire_type, itself a varint.

    PbWriter::write_varint

    fn PbWriter::write_varint(self : PbWriter, value : UInt64) -> Unit

    Append a base-128 varint (protobuf "Base 128 Varints"): seven bits per octet, little-endian groups, the high bit marking continuation.

    Priority

    pub(all) struct Priority {
    exclusive : Bool
    stream_dependency : Int
    weight : Int
    } derive(Eq)

    The stream-priority block shared by PRIORITY frames and the optional priority section of HEADERS (RFC 7540 §5.3.2). weight is the raw wire byte (0..255); the effective priority weight is weight + 1 (§6.3).

    ReflectionRequest

    pub(all) enum ReflectionRequest {
    ListServices(String)
    FileByFilename(String)
    FileContainingSymbol(String)
    Unsupported
    } derive(Eq)

    A decoded ServerReflectionRequest, reduced to the one message_request oneof arm that was set. Extension-number queries are surfaced as Unsupported, which the service answers with an UNIMPLEMENTED error response.

    ReflectionResponse

    pub(all) enum ReflectionResponse {
    ServiceList(Array[String])
    FileDescriptors(Array[Bytes])
    ReflectionError(Int, String)
    } derive(Eq)

    A decoded ServerReflectionResponse, reduced to the one message_response arm.

    ReflectionService

    pub struct ReflectionService {
    files : Array[FileDescriptor]
    by_symbol : Map[String, Int]
    by_filename : Map[String, Int]
    services : Array[String]
    }

    A grpc.reflection.v1.ServerReflection service backed by an in-memory descriptor database. Each added FileDescriptor is indexed by filename and by every symbol (package.Service / package.Message) it defines, so a FileContainingSymbol or FileByFilename query resolves to the right file, and ListServices enumerates every registered service.

    ReflectionService::add_file

    fn ReflectionService::add_file(self : ReflectionService, file : FileDescriptor) -> Unit

    Register a file descriptor: index it by filename and by each symbol it defines, and add its services to the ListServices set.

    ReflectionService::handle

    fn ReflectionService::handle(self : ReflectionService, request : Bytes) -> Bytes

    Answer one ServerReflectionRequest (raw bytes) with the encoded ServerReflectionResponse. A decode failure or an unknown symbol/filename yields an ErrorResponse rather than raising, since it rides a non-raising stream handler.

    ReflectionService::handler

    The bidi handler backing ServerReflectionInfo: one response per request, none at half-close.

    ReflectionService::install

    fn ReflectionService::install(self : ReflectionService, server : H2Server) -> Unit

    Register ServerReflectionInfo at both the v1 and v1alpha paths on a pure server engine.

    ReflectionService::new

    An empty reflection service. Add the descriptors of the services you serve with add_file.

    RetryPolicy

    pub(all) struct RetryPolicy {
    max_attempts : Int
    initial_backoff_millis : Int
    max_backoff_millis : Int
    backoff_multiplier : Double
    retryable : Array[Int]
    }

    A method's retry policy. max_attempts counts the first try plus every retry (gRPC caps it at 5); a call is retried only while attempts remain and its status is in retryable. Backoff starts at initial_backoff_millis and grows by backoff_multiplier each retry, capped at max_backoff_millis.

    RetryPolicy::backoff_millis

    fn RetryPolicy::backoff_millis(self : RetryPolicy, retry_index : Int) -> Int

    The backoff cap before the retry_index-th retry (the first retry is 1): min(initial * multiplier^(retry_index-1), max). gRPC sleeps a value drawn uniformly from [0, cap]; the async driver applies that jitter, so this returns the deterministic upper bound (which is also what a jitter-free driver sleeps).

    RetryPolicy::default

    fn RetryPolicy::default() -> RetryPolicy

    A conventional default: up to 3 attempts, 100 ms initial backoff doubling to a 1 s cap, retrying only UNAVAILABLE — the code a transient, safe-to-retry transport failure carries.

    RetryPolicy::should_retry

    fn RetryPolicy::should_retry(self : RetryPolicy, status_code : Int, attempts_made : Int) -> Bool

    Whether a call that has made attempts_made attempts (the first is 1) and got status_code should be tried again: attempts must remain, and the status must be non-OK and listed as retryable.

    RpcContext

    pub(all) struct RpcContext {
    path : String
    metadata : Array[Header]
    deadline_millis : Int?
    resp_headers : Array[Header]
    resp_trailers : Array[Header]
    status_fail : (Int, String)?
    error_details : Array[Bytes]
    }

    The context surfaced to a handler for one RPC call: the invoked :path, the request metadata (custom HEADERS, minus the pseudo- and reserved gRPC headers), the deadline parsed from grpc-timeout (in milliseconds, None when absent), and mutable slots for response initial metadata and trailing metadata the handler can set before it returns.

    RpcContext::add_error_detail

    fn RpcContext::add_error_detail(self : RpcContext, detail : Bytes) -> Unit

    Attach a rich-error detail (a serialized google.protobuf.Any) to a failing call. The server packs the status code, message, and every detail into a google.rpc.Status and sends it base64-encoded in the grpc-status-details-bin trailer — gRPC's rich-error channel. Combine with fail, which sets the base grpc-status / grpc-message.

    RpcContext::add_header

    fn RpcContext::add_header(self : RpcContext, name : Bytes, value : Bytes) -> Unit

    Add an initial-metadata header to the response. Only takes effect if called before the response HEADERS are flushed (any point inside a unary / server- / client-streaming handler, or inside a bidi factory before the first message).

    RpcContext::add_trailer

    fn RpcContext::add_trailer(self : RpcContext, name : Bytes, value : Bytes) -> Unit

    Add a trailing-metadata header, sent in the trailer HEADERS alongside grpc-status.

    RpcContext::empty

    fn RpcContext::empty() -> RpcContext

    A context with no path, metadata, or deadline — the placeholder a stream holds until its request HEADERS are decoded.

    RpcContext::fail

    fn RpcContext::fail(self : RpcContext, code : Int, message : String) -> Unit

    End the call with a non-OK gRPC status. A handler calls this (e.g. ctx.fail(Status::code(NotFound), "user 42 does not exist")) instead of returning a normal reply; the server sends the grpc-status / grpc-message trailer and drops the reply message. The five common codes have named helpers on [Status]; any code is accepted.

    RpcContext::metadata_get

    fn RpcContext::metadata_get(self : RpcContext, name : Bytes) -> Bytes?

    The value of request metadata name, or None. Names are matched byte-for-byte (gRPC lowercases header names on the wire), so binary -bin metadata works too.

    ServiceDescriptor

    pub(all) struct ServiceDescriptor {
    name : String
    methods : Array[MethodDescriptor]
    } derive(Eq)

    A service (ServiceDescriptorProto): its (simple) name and its methods.

    ServiceDescriptor::decode

    fn ServiceDescriptor::decode(body : Bytes) -> ServiceDescriptor raise PbError

    Decode a ServiceDescriptorProto.

    ServiceDescriptor::encode

    fn ServiceDescriptor::encode(self : ServiceDescriptor) -> Bytes

    Encode a ServiceDescriptorProto.

    ServingStatus

    pub(all) enum ServingStatus {
    StatusUnknown
    Serving
    NotServing
    ServiceUnknown
    } derive(Eq)

    The grpc.health.v1.HealthCheckResponse.ServingStatus enum.

    ServingStatus::code

    fn ServingStatus::code(self : ServingStatus) -> Int

    The wire value of a serving status (the protobuf enum number).

    ServingStatus::from_code

    fn ServingStatus::from_code(n : Int) -> ServingStatus

    The serving status for a protobuf enum number; out-of-range numbers read as StatusUnknown.

    SrvStream

    pub(all) struct SrvStream {
    id : Int
    state : StreamState
    header_block :
    Buffer

    data :
    Buffer

    req_off : Int
    req_msgs : Array[Bytes]
    bidi_processed : Int
    headers_complete : Bool
    in_headers : Bool
    end_stream_recv : Bool
    path : String
    ctx : RpcContext
    deadline_at : Int64?
    recv_window : Int
    send_window : Int
    out : Bytes
    out_off : Int
    started : Bool
    started_response : Bool
    response_ended : Bool
    finalized : Bool
    trailers_only : Bool
    http_status : Int
    status_code : Int
    status_message : String
    req_encoding : Bytes
    compressed_unsupported : Bool
    oversize : Bool
    headers_sent : Bool
    trailers_sent : Bool
    bidi : BidiHandler?
    }

    One server-side stream: its lifecycle state, the accumulating request header block and DATA (with a cursor over the length-prefixed messages already pulled out of it), the per-stream flow-control windows, and the response side — the bytes still to send, whether the initial HEADERS and the trailers have gone out, and any live bidi call state.

    Status

    pub(all) enum Status {
    Ok
    Cancelled
    Unknown
    InvalidArgument
    DeadlineExceeded
    NotFound
    AlreadyExists
    PermissionDenied
    ResourceExhausted
    FailedPrecondition
    Aborted
    OutOfRange
    Unimplemented
    Internal
    Unavailable
    DataLoss
    Unauthenticated
    } derive(Eq)

    The 17 canonical gRPC status codes (grpc-status).

    Status::code

    fn Status::code(self : Status) -> Int

    The numeric grpc-status code.

    Status::from_h2_error

    fn Status::from_h2_error(code : Int) -> Status

    The status a call ends with when the peer resets its stream instead of sending trailers, from the RST_STREAM error code (gRPC PROTOCOL-HTTP2, "HTTP2 Error Code → Status"). A refused stream was never processed, so it is UNAVAILABLE and safe to retry; anything the table does not name is INTERNAL, since the peer broke the transport rather than the call.

    Status::from_http

    fn Status::from_http(code : Int) -> Status

    The status a non-200 HTTP response maps to when it carries no grpc-status — a proxy or a plain HTTP server answering on the gRPC port (gRPC http-grpc-status-mapping). Anything outside the table is UNKNOWN.

    Status::name

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

    The canonical uppercase status name.

    Stream

    pub(all) struct Stream {
    id : Int
    state : StreamState
    }

    A mutable stream: its identifier and current lifecycle state. send/recv advance the state in place, raising on an illegal transition.

    Stream::new

    fn Stream::new(id : Int) -> Stream

    A fresh idle stream with the given identifier.

    Stream::recv

    fn Stream::recv(self : Stream, ev : StreamEvent) -> StreamState raise StreamError

    Advance this stream by receiving ev, returning the new state.

    Stream::send

    fn Stream::send(self : Stream, ev : StreamEvent) -> StreamState raise StreamError

    Advance this stream by sending ev, returning the new state.

    StreamEvent

    pub(all) enum StreamEvent {
    Headers(end_stream~ : Bool)
    Data(end_stream~ : Bool)
    Reserve
    RstStream
    } derive(Eq)

    A state-changing stream event: the arrival or departure of the frames that drive §5.1 transitions. Headers/Data carry the END_STREAM flag; Reserve is a PUSH_PROMISE reserving this (promised) stream. Frames that never change stream state (PRIORITY, WINDOW_UPDATE, SETTINGS, PING) are intentionally absent.

    StreamState

    pub(all) enum StreamState {
    Idle
    ReservedLocal
    ReservedRemote
    Open
    HalfClosedLocal
    HalfClosedRemote
    Closed
    } derive(Eq,
    Debug
    )

    The lifecycle state of a single HTTP/2 stream (RFC 7540 §5.1).
    impl Show for StreamState

    StreamState::on_recv

    fn StreamState::on_recv(self : StreamState, ev : StreamEvent) -> StreamState raise StreamError

    The next state after receiving ev in this state (RFC 7540 §5.1, remote side — the mirror of on_send). Raises InvalidTransition on an illegal frame.

    StreamState::on_send

    fn StreamState::on_send(self : StreamState, ev : StreamEvent) -> StreamState raise StreamError

    The next state after sending ev from this state (RFC 7540 §5.1, local side). Raises InvalidTransition for a frame illegal in the current state.

    SubConn

    pub struct SubConn {
    address : String
    state : ConnectivityState
    }

    One address the Channel dials, tracking its connectivity state.

    SubConn::connect

    fn SubConn::connect(self : SubConn) -> Unit

    Begin connecting (IDLE or TRANSIENT_FAILURE → CONNECTING). A no-op once SHUTDOWN or already connecting/ready.

    SubConn::is_ready

    fn SubConn::is_ready(self : SubConn) -> Bool

    Whether this sub-connection can carry RPCs right now.

    SubConn::new

    fn SubConn::new(address : String) -> SubConn

    A fresh sub-connection to address, starting IDLE.

    SubConn::on_connected

    fn SubConn::on_connected(self : SubConn) -> Unit

    The connection attempt succeeded (CONNECTING → READY).

    SubConn::on_failure

    fn SubConn::on_failure(self : SubConn) -> Unit

    A connection attempt or an established connection failed (→ TRANSIENT_FAILURE), unless shut down.

    SubConn::on_idle

    fn SubConn::on_idle(self : SubConn) -> Unit

    A READY connection went idle (READY → IDLE).

    SubConn::shutdown

    fn SubConn::shutdown(self : SubConn) -> Unit

    Shut the sub-connection down permanently (→ SHUTDOWN, a terminal state).

    WireType

    pub(all) enum WireType {
    Varint
    Fixed64
    LengthDelim
    Fixed32
    } derive(Eq)

    The four protobuf wire types carried by a field tag's low three bits. The two group types (3 start-group, 4 end-group) are deprecated and unsupported, so from_code rejects them.
    impl Show for WireType

    WireType::code

    fn WireType::code(self : WireType) -> Int

    The wire-type number (the tag's low three bits).

    WireType::from_code

    fn WireType::from_code(n : Int) -> WireType?

    The wire type for a tag's low three bits, or None for the deprecated group types (3/4) and any out-of-range value.

    base64_decode

    fn base64_decode(data : Bytes) -> Bytes

    Standard base64 decode. Non-alphabet bytes (padding, whitespace) are skipped, so both padded and unpadded input decode.

    base64_encode

    fn base64_encode(data : Bytes) -> Bytes

    Standard base64 encode with = padding.

    connection_preface

    let connection_preface : Bytes

    The HTTP/2 client connection preface (RFC 7540 §3.5): the fixed, case-sensitive 24-octet sequence PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n a client sends before its first frame, which must be a SETTINGS frame. Its bytes deliberately form a malformed HTTP/1.1 request so an HTTP/1.1-only server rejects it cleanly.

    decode_file_descriptor_set

    fn decode_file_descriptor_set(body : Bytes) -> Array[FileDescriptor] raise PbError

    Decode a FileDescriptorSet into its files.

    decode_frame

    fn decode_frame(data : Bytes, offset? : Int) -> (Frame, Int) raise FrameError

    Decode exactly one frame at offset, returning (frame, bytes_consumed) where bytes_consumed is 9 + payload_length. Raises Incomplete when the buffer does not yet hold the whole frame, or a protocol error when the payload is malformed for its type. The inverse of Frame::encode.

    decode_frame_header

    fn decode_frame_header(data : Bytes, offset? : Int) -> FrameHeader raise FrameError

    Decode the 9-octet frame header at offset. Raises Incomplete when fewer than 9 octets are available. The reserved bit is masked off the stream id.

    decode_health_request

    fn decode_health_request(msg : Bytes) -> Bytes

    Decode the service field of a HealthCheckRequest, or empty bytes when the field is absent (the overall-server check). Unknown fields are skipped.

    decode_health_response

    fn decode_health_response(msg : Bytes) -> ServingStatus

    Decode the status field of a HealthCheckResponse; an absent field reads as the 0 default (StatusUnknown).

    decode_message

    fn decode_message(data : Bytes) -> (Bool, Bytes)?

    Decode one gRPC length-prefixed message from the front of data, returning (compressed, payload), or None if fewer than a full frame is present.

    decode_reflection_request

    fn decode_reflection_request(body : Bytes) -> ReflectionRequest raise PbError

    Decode a ServerReflectionRequest down to its message_request oneof arm. host (field 1) is accepted and ignored; an unset oneof reads as Unsupported.

    decode_reflection_response

    fn decode_reflection_response(body : Bytes) -> ReflectionResponse raise PbError

    Decode a ServerReflectionResponse down to its message_response arm — the half an in-process reflection client (and the tests) needs to read an answer.

    default_max_frame_size

    let default_max_frame_size : Int

    The HTTP/2 default (and minimum) SETTINGS_MAX_FRAME_SIZE (RFC 7540 §6.5.2).

    default_max_streams

    let default_max_streams : Int

    The SETTINGS_MAX_CONCURRENT_STREAMS the server advertises and enforces unless a caller says otherwise: 100, the value gRPC and nghttp2 both settle on. RFC 9113 §5.1.2 leaves the setting unset by default, which means unbounded — and every live stream holds a request buffer, so a peer that opens streams and never finishes them pins memory for as long as the connection lasts.

    default_window_size

    let default_window_size : Int

    The HTTP/2 default flow-control window and initial SETTINGS_INITIAL_WINDOW_SIZE (RFC 7540 §6.9.2): 65 535 octets.

    dns_decode_response

    fn dns_decode_response(msg : Bytes) -> DnsResponse

    Decode a DNS response: echoed id, response code, and every answer record (A/AAAA records carry their textual address). Questions are skipped; authority and additional sections are ignored.

    dns_encode_query

    fn dns_encode_query(id : Int, name : String, qtype : Int) -> Bytes

    Encode a standard recursion-desired query for name of type qtype (dns_type_a or dns_type_aaaa) in class IN, with transaction id id.

    dns_type_a

    let dns_type_a : Int

    The DNS record type for an IPv4 address.

    dns_type_aaaa

    let dns_type_aaaa : Int

    The DNS record type for an IPv6 address.

    encode_file_descriptor_set

    fn encode_file_descriptor_set(files : Array[FileDescriptor]) -> Bytes

    Encode a FileDescriptorSet (protoc --descriptor_set_out): the concatenation of FileDescriptorProtos under repeated field 1.

    encode_grpc_timeout

    fn encode_grpc_timeout(millis : Int) -> Bytes

    Encode whole millis as a grpc-timeout header value (RFC gRPC HTTP/2 mapping): the m (millisecond) unit when the count fits the 8-digit field, else seconds with the S unit.

    encode_health_request

    fn encode_health_request(service : Bytes) -> Bytes

    Encode a HealthCheckRequest: field 1 (service, a length-delimited string). An empty service name encodes to the empty message, the wire form of the overall-server check.

    encode_health_response

    fn encode_health_response(status : ServingStatus) -> Bytes

    Encode a HealthCheckResponse: field 1 (status, a varint enum). The SERVING default 0 still encodes to the empty message per protobuf default-omission.

    encode_message

    fn encode_message(payload : Bytes, compressed? : Bool) -> Bytes

    Encode a payload as a gRPC Length-Prefixed-Message: a 1-byte compression flag, a 4-byte big-endian length, then the payload. This is the framing every gRPC transport shares (gRPC-Web over HTTP/1.1 and real gRPC over HTTP/2 alike).

    encode_reflection_request

    fn encode_reflection_request(req : ReflectionRequest) -> Bytes

    Encode a ServerReflectionRequest carrying a single oneof arm. Used by an in-process reflection client (and by the round-trip tests).

    error_cancel

    let error_cancel : Int

    CANCEL: the stream is no longer wanted.

    error_compression_error

    let error_compression_error : Int

    COMPRESSION_ERROR: the HPACK context is corrupt, which kills the connection.

    error_connect_error

    let error_connect_error : Int

    CONNECT_ERROR: the TCP connection behind a CONNECT stream failed.

    error_enhance_your_calm

    let error_enhance_your_calm : Int

    ENHANCE_YOUR_CALM: the peer is generating too much load.

    error_flow_control_error

    let error_flow_control_error : Int

    FLOW_CONTROL_ERROR: the peer sent more than its credit allowed.

    error_frame_size_error

    let error_frame_size_error : Int

    FRAME_SIZE_ERROR: the frame's length is invalid for its type.

    error_http_1_1_required

    let error_http_1_1_required : Int

    HTTP_1_1_REQUIRED: the request cannot be served over HTTP/2.

    error_inadequate_security

    let error_inadequate_security : Int

    INADEQUATE_SECURITY: the transport does not meet the minimum this endpoint requires.

    error_internal_error

    let error_internal_error : Int

    INTERNAL_ERROR: the endpoint failed for reasons of its own.

    error_no_error

    let error_no_error : Int

    HTTP/2 error codes (RFC 7540 §7), carried by RST_STREAM and GOAWAY.

    error_protocol_error

    let error_protocol_error : Int

    PROTOCOL_ERROR: the peer broke the protocol in a way not covered below.

    error_refused_stream

    let error_refused_stream : Int

    REFUSED_STREAM: the stream was declined before any processing, so it is safe to retry.

    error_settings_timeout

    let error_settings_timeout : Int

    SETTINGS_TIMEOUT: a SETTINGS frame went unacknowledged too long.

    error_stream_closed

    let error_stream_closed : Int

    STREAM_CLOSED: a frame arrived for a stream that was already finished.

    flag_ack

    let flag_ack : Int

    ACK on SETTINGS and PING shares bit 0x1 with END_STREAM.

    flag_end_headers

    let flag_end_headers : Int

    END_HEADERS: no CONTINUATION follows this header block.

    flag_end_stream

    let flag_end_stream : Int

    HTTP/2 frame flags (RFC 7540 §6). Flags are type-specific; the same bit carries different meaning per frame type, hence the shared numeric values.

    flag_padded

    let flag_padded : Int

    PADDED: a length byte and that many padding bytes wrap the payload.

    flag_priority

    let flag_priority : Int

    PRIORITY: this HEADERS frame carries a stream-dependency block.

    frame_continuation

    let frame_continuation : Int

    CONTINUATION: the rest of a header block too large for one frame.

    frame_data

    let frame_data : Int

    HTTP/2 frame type codes (RFC 7540 §6).

    frame_goaway

    let frame_goaway : Int

    GOAWAY: stop opening streams, and here is the last id I accepted.

    frame_headers

    let frame_headers : Int

    HEADERS: opens a stream and carries its HPACK header block.

    frame_ping

    let frame_ping : Int

    PING: the round-trip probe keepalive is built on.

    frame_priority

    let frame_priority : Int

    PRIORITY: the deprecated stream-dependency hint; accepted and ignored.

    frame_push_promise

    let frame_push_promise : Int

    PUSH_PROMISE: server push, which gRPC never uses.

    frame_rst_stream

    let frame_rst_stream : Int

    RST_STREAM: abort one stream without touching the connection.

    frame_settings

    let frame_settings : Int

    SETTINGS: the connection parameters each peer announces, and their ack.

    frame_window_update

    let frame_window_update : Int

    WINDOW_UPDATE: hand the peer more flow-control credit.

    gunzip

    fn gunzip(data : Bytes) -> Bytes raise GzError

    Decode a single gzip member (RFC 1952): validate the 10-byte header, skip the optional EXTRA / NAME / COMMENT / HCRC fields, inflate the DEFLATE body, and verify the trailing CRC-32 and ISIZE. Raises [GzError] on any malformation.

    has_connection_preface

    fn has_connection_preface(data : Bytes) -> Bool

    Whether data begins with the exact 24-octet HTTP/2 connection preface.

    health_check_path

    let health_check_path : String

    The gRPC HTTP/2 path of the Check method.

    health_watch_path

    let health_watch_path : String

    The gRPC HTTP/2 path of the Watch method.

    hpack_decode_int

    fn hpack_decode_int(data : Bytes, offset : Int, prefix_bits : Int) -> (Int, Int) raise HpackError

    Decode an HPACK integer with an prefix_bits-bit prefix from data starting at offset (RFC 7541 §5.1), returning (value, bytes_consumed). Any flag bits above the prefix in the first octet are masked off and ignored.

    hpack_decode_string

    fn hpack_decode_string(data : Bytes, offset : Int) -> (Bytes, Int) raise HpackError

    Decode an HPACK string literal from data at offset, returning (octets,bytes_consumed). The length is read as a 7-bit-prefix integer (the H bit is masked off); this is the inverse of hpack_encode_string for H = 0. Use hpack_string_is_huffman first if the literal may be Huffman-coded, as Huffman decoding is not applied here.

    hpack_encode_int

    fn hpack_encode_int(value : Int, prefix_bits : Int) -> Bytes

    Encode value as an HPACK integer with an prefix_bits-bit prefix (RFC 7541 §5.1). The high 8 - prefix_bits bits of the first octet are left zero for the caller to OR in any flag bits. Examples: 10 on a 5-bit prefix is [0x0A]; 1337 on a 5-bit prefix is [0x1F, 0x9A, 0x0A].

    hpack_encode_size_update

    fn hpack_encode_size_update(new_max : Int) -> Bytes

    Encode a dynamic table size update (RFC 7541 §6.3): 001 prefix with the new maximum size as a 5-bit-prefix integer.

    hpack_encode_string

    fn hpack_encode_string(octets : Bytes) -> Bytes

    Encode octets as a non-Huffman HPACK string literal (RFC 7541 §5.2): the length as a 7-bit-prefix integer with the H bit clear, followed by the raw octets.

    hpack_encode_string_auto

    fn hpack_encode_string_auto(octets : Bytes) -> Bytes

    Encode octets as an HPACK string literal, choosing the shorter of the raw (H = 0) and Huffman (H = 1) forms — the standard encoder heuristic.

    hpack_encode_string_huffman

    fn hpack_encode_string_huffman(octets : Bytes) -> Bytes

    Encode octets as a Huffman-coded HPACK string literal (RFC 7541 §5.2): the H bit set, the Huffman length as a 7-bit-prefix integer, then the code.

    hpack_read_string

    fn hpack_read_string(data : Bytes, offset : Int) -> (Bytes, Int) raise HpackError

    Read an HPACK string literal at offset, resolving Huffman coding when the H bit is set, returning (octets, bytes_consumed). Unlike hpack_decode_string, this applies Huffman decoding. Raises on bad Huffman.

    hpack_static_entry

    fn hpack_static_entry(index : Int) -> (String, String)?

    Look up an HPACK static-table entry by its 1-based RFC index (1..=61), returning (name, value) or None when the index is out of range.

    hpack_static_table

    fn hpack_static_table() -> Array[(String, String)]

    The 61-entry HPACK static header table (RFC 7541, Appendix A) as (name,value) pairs in RFC index order, i.e. result[0] is index 1 (:authority) and result[60] is index 61 (www-authenticate).

    hpack_string_is_huffman

    fn hpack_string_is_huffman(data : Bytes, offset : Int) -> Bool

    Whether the string literal at offset is Huffman-coded, i.e. the H bit (the top bit of the length octet) is set (RFC 7541 §5.2).

    huffman_decode

    fn huffman_decode(input : Bytes) -> Bytes raise HpackError

    Huffman-decode input (RFC 7541 §5.2). Raises HuffmanError if the input contains the EOS symbol, if the trailing padding is not a run of fewer than 8 one-bits, or if the bit stream leaves the code space. The inverse of huffman_encode.

    huffman_encode

    fn huffman_encode(input : Bytes) -> Bytes

    Huffman-encode input (RFC 7541 §5.2): each octet becomes its code, and the final partial octet is padded with the most-significant bits of the EOS code (all ones). The inverse of huffman_decode for valid inputs.

    huffman_encoded_length

    fn huffman_encoded_length(input : Bytes) -> Int

    The number of octets input occupies when Huffman-encoded, without building the output — used to choose the shorter of raw vs. Huffman string literals.

    is_binary_metadata

    fn is_binary_metadata(name : Bytes) -> Bool

    Whether a metadata key names binary content: it ends in -bin (gRPC's convention for base64-on-the-wire values).

    max_header_list_size

    let max_header_list_size : Int

    The cap on one accumulated header block (HEADERS plus its CONTINUATION frames). Without it a peer could stream endless non-final CONTINUATION frames and grow the buffer without bound (a CONTINUATION flood); 128 KiB is far above any real gRPC request's headers.

    max_message_size

    let max_message_size : Int

    The default cap on a single received gRPC message (4 MiB, matching gRPC's default MaxRecvMsgSize). A length prefix above this — including one whose 4 bytes decode to a negative Int because the high bit is set — is rejected rather than trusted, so a hostile prefix can neither slice out of bounds nor pin unbounded buffer.

    metadata_value_from_wire

    fn metadata_value_from_wire(name : Bytes, value : Bytes) -> Bytes

    Decode a metadata value coming off the wire: base64-decoded for a -bin key, verbatim otherwise.

    metadata_value_to_wire

    fn metadata_value_to_wire(name : Bytes, value : Bytes) -> Bytes

    Encode a metadata value for the wire: base64-encoded for a -bin key, verbatim otherwise.

    parse_grpc_timeout

    fn parse_grpc_timeout(v : Bytes) -> Int?

    Parse a grpc-timeout value (RFC: up to 8 ASCII digits then a unit — H/M/S/m/u/n) to whole milliseconds, flooring sub-millisecond units. None for a malformed value.

    percent_decode

    fn percent_decode(v : Bytes) -> String

    Percent-decode a received grpc-message value — the inverse of the server's percent_encode (gRPC spec §"Responses"): each %XX escape becomes the byte its two hex digits name, every other byte passes through, and a malformed escape is left literal; the result is read as UTF-8.

    reflection_v1_path

    let reflection_v1_path : String

    The gRPC HTTP/2 path of the v1 reflection stream.

    reflection_v1alpha_path

    let reflection_v1alpha_path : String

    The gRPC HTTP/2 path of the legacy v1alpha reflection stream. grpcurl tries v1 first and falls back to this, so a server registers both.

    settings_enable_push

    let settings_enable_push : Int

    SETTINGS_ENABLE_PUSH: whether the peer may send PUSH_PROMISE.

    settings_header_table_size

    let settings_header_table_size : Int

    SETTINGS parameter identifiers (RFC 7540 §6.5.2).

    settings_initial_window_size

    let settings_initial_window_size : Int

    SETTINGS_INITIAL_WINDOW_SIZE: the flow-control credit a new stream starts with.

    settings_max_concurrent_streams

    let settings_max_concurrent_streams : Int

    SETTINGS_MAX_CONCURRENT_STREAMS: how many streams may be open at once.

    settings_max_frame_size

    let settings_max_frame_size : Int

    SETTINGS_MAX_FRAME_SIZE: the largest payload the peer will accept in one frame.

    settings_max_header_list_size

    let settings_max_header_list_size : Int

    SETTINGS_MAX_HEADER_LIST_SIZE: the advisory ceiling on a decoded header list.

    stream_id_valid_for_initiator

    fn stream_id_valid_for_initiator(id : Int, by_client~ : Bool) -> Bool

    Whether a peer that is a client (by_client = true) or server may legally open stream id: clients use odd ids, servers use even ids, and 0 is the connection control stream, openable by neither (RFC 7540 §5.1.1).

    stream_is_client_initiated

    fn stream_is_client_initiated(id : Int) -> Bool

    Whether id is a client-initiated stream: a non-zero odd identifier.

    stream_is_server_initiated

    fn stream_is_server_initiated(id : Int) -> Bool

    Whether id is a server-initiated (pushed) stream: a non-zero even identifier.