Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

rpc.endpoint

import rpc

Everything here is re-exported by rpc, so import rpc is enough and the names are called as rpc.*. Importing rpc.endpoint on its own works too and reaches the same definitions.

Endpoint: one side of a JSON-RPC connection. It is a Service on a connection: it answers the requests and notifications the other side sends, and sends its own. JSON-RPC makes no difference between a client and a server, so the same endpoint can do both on one connection.

Re-exported by rpc.

Classes

Endpoint

class rpc.Endpoint < Service

One side of a JSON-RPC connection over a transport.

An endpoint is a Service, so its handlers, its fallback and its batch limit work exactly as a service’s do. The connection adds the other direction: an endpoint calls the other side with request(), request_batch(), send_request() and notify(), and a handler on one can defer() its answer and send it later.

import rpc
import isolate

var ends = rpc.pipe()

# The server runs on an isolate of its own, answering until the
# client closes its end.
isolate.spawn(@(transport) {
  import rpc

  rpc.endpoint(transport)
    .on_request('add', @(params) => params[0] + params[1])
    .serve()
}, ends[1])

var client = rpc.endpoint(ends[0])

echo client.request('add', [2, 3])  # 5
client.close()

An endpoint reads its transport in one of three ways. serve() reads and answers until the connection ends, on the isolate that calls it. listen() reads on an isolate of its own and returns a Channel of what arrives, for a program that waits on other things as well; it passes each one to dispatch() when it is ready to handle it. A program that reads the transport itself, such as one polling many connections, hands what it reads to feed(). Every handler runs on the isolate that owns the endpoint, and everything is sent from there.

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

rpc.Endpoint(transport)

Returns a new Endpoint over transport. Its messages are framed the way the transport keeps them apart: with the transport’s own framing() when it has one, as a WebSocket does, and with a HeaderFraming otherwise.

Parameters

  • transport (any) — A transport; see rpc.transport.

Endpoint.set_framing()

rpc.Endpoint.set_framing(framing) -> Endpoint

Sets how messages are told apart in the stream. Set it before listen(), which hands the framing to the isolate it reads on.

Parameters

  • framing (HeaderFraming|LineFraming|MessageFraming) — Default the transport’s own framing(), or HeaderFraming().

Returns Endpoint — itself.

Endpoint.notify()

rpc.Endpoint.notify(method: string, params)

Sends a notification: calls method on the other side and expects nothing back.

Parameters

  • method (string)
  • params (list|dict|nil) — Default nil, which sends none.

Raises RpcClosedError when the endpoint has been closed.

Endpoint.send_request()

rpc.Endpoint.send_request(method: string, params, callback: function)

Sends a request and returns at once; callback is called with the answer when it arrives, on the isolate that owns the endpoint, while it serves, waits on a request, or dispatches.

Parameters

  • method (string)
  • params (list|dict|nil)
  • callback (function(2)) — Called with the result and nil, or with nil and the error: an {RpcError} the other side answered with, or an {RpcClosedError} when the connection ended first.

Returns — int: The request’s id.

Raises RpcClosedError when the endpoint has been closed.

Endpoint.request()

rpc.Endpoint.request(method: string, params, timeout: ?number)

Sends a request and waits for its answer, handling whatever else arrives in the meantime as serve() would.

var sum = endpoint.request('add', [2, 3])

An error answer that names no request, which the other side sends for a message it cannot read at all, goes to on_error() rather than to any one request.

Parameters

  • method (string)
  • params (list|dict|nil)
  • timeout (?number) — The most seconds to wait. Default nil, which waits until the answer comes or the connection ends. A timeout needs the endpoint to be listening; one reading its transport itself waits as long as the transport’s reads do.

Returns — any: The result the other side answered with.

Raises RpcError when the other side answers with an error.

Raises RpcClosedError when the endpoint has been closed, or the connection ends before the answer.

Raises RpcTimeoutError when timeout runs out first.

Endpoint.request_batch()

rpc.Endpoint.request_batch(calls: list, timeout: ?number)

Sends several requests as one batch and waits for every answer, handling whatever else arrives in the meantime as request() does. The other side may answer them in any order; they come back in the order of calls.

var sums = endpoint.request_batch([['add', [1, 2]], ['add', [3, 4]]])

Parameters

  • calls (list) — Each call as a pair, [method, params].
  • timeout (?number) — The most seconds to wait for every answer, as for request().

Returns — list: For each call, in order, the result the other side answered with, or the {RpcError} it answered with.

Raises ValueError when calls is empty, or a call is not a [method, params] pair.

Raises RpcClosedError when the endpoint has been closed, or the connection ends before every answer.

Raises RpcTimeoutError when timeout runs out first.

Endpoint.send()

rpc.Endpoint.send(message)

Sends a message as it is: a Request, a Notification, a Response, or a list of them as a batch.

Parameters

  • message (Request|Notification|Response|list)

Raises RpcClosedError when the endpoint has been closed.

Endpoint.serve()

rpc.Endpoint.serve()

Reads and answers messages until the connection ends, stop() is called, or the endpoint is closed. On a listening endpoint, it takes what arrives from the Channel listen() returned.

A message that is not valid UTF-8, or not valid JSON, is answered with PARSE_ERROR and an id of nil.

Raises RpcFramingError when the stream cannot be read past a message.

Endpoint.stop()

rpc.Endpoint.stop()

Makes serve() return once the message it is handling is done.

Endpoint.listen()

rpc.Endpoint.listen() -> isolate.Channel

Starts reading the transport on an isolate of its own, and returns the Channel each message is put on as it arrives, already decoded from JSON. Pass each item taken from it to dispatch(), on the isolate that owns the endpoint, when the program is ready to handle it.

var inbox = endpoint.listen()

while !endpoint.is_closed() {
  var ready = isolate.select([inbox, work], 0.5)

  if ready[0] == inbox {
    endpoint.dispatch(ready[1])
  }
}

Each item is a dictionary whose kind says what arrived:

  • { kind: 'message', text, value }: a message, as its JSON text and the value decoded from it.
  • { kind: 'unreadable', text, message }: a message that is not valid JSON, with text nil when it is not valid UTF-8 either, and the parse error it is answered with.
  • { kind: 'closed' }: the end of the connection.
  • { kind: 'failed', message }: a failure reading the transport, which ends the connection.

Calling listen() again returns the same Channel.

Returns isolate.Channel

Raises ValueError when the transport cannot be read from another isolate; see can_listen() in rpc.transport.

Endpoint.dispatch()

rpc.Endpoint.dispatch(item: dict)

Handles one item taken from the Channel listen() returned: answers a message, or marks the connection ended. A failed connection is reported to on_error() with an RpcClosedError.

Parameters

  • item (dict)

Endpoint.handle()

rpc.Endpoint.handle(value)

Handles one message already decoded from JSON, or a batch of them, exactly as if it had just arrived, and sends whatever answer it has.

Parameters

  • value (any) — A value as json.decode() returns it.

Endpoint.feed()

rpc.Endpoint.feed(data: bytes)

Handles every message data completes, for a program that reads the transport itself rather than through serve() or listen(), such as one polling many connections. Empty data ends the connection, as an empty read does.

Parameters

  • data (bytes) — The next bytes read from the transport.

Raises RpcFramingError when the stream cannot be read past a message.

Endpoint.close()

rpc.Endpoint.close()

Closes the transport. Nothing more can be sent, and every request still waiting is answered with an RpcClosedError.

Endpoint.is_closed()

rpc.Endpoint.is_closed() -> bool

True once the endpoint is closed or the connection has ended.

Returns bool

Endpoint.to_string()

rpc.Endpoint.to_string() -> string

This endpoint as Endpoint(transport).

Returns string


2026, Richard Ore and Zuri contributors