Sign in

    moonbitstack/moonrpc/server does not have a README file

    StreamInterceptor

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

    UnaryInterceptor

    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.

    H2Server

    pub struct H2Server {
    handlers : Map[String,
    Handler
    ]
    encoder :
    Encoder

    decoder :
    Decoder

    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

    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

    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 @http2.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

    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 @http2.preface and the peer is told exactly what it will be held to.

    H2Server::stream_state

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

    H2Server::tick

    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.

    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.

    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.