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.framing

import rpc

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

How messages are told apart in a stream of bytes. A stream carries one message after another with nothing to mark where one ends, so each is framed: HeaderFraming puts a header giving its length in front of it, and LineFraming ends it with a line break. A transport that already keeps messages apart, as a WebSocket does, needs no framing of its own, and MessageFraming takes each of its reads as one whole message.

A framing is fed the bytes as they arrive, in whatever pieces they arrive in, and hands back each message once the whole of it is there. It frames the other way too, turning a message’s text into the bytes to send.

Re-exported by rpc.

Constants

DEFAULT_MAX_SIZE

rpc.DEFAULT_MAX_SIZE = 67108864

The largest message a framing accepts unless told otherwise: 64 MiB.

Classes

HeaderFraming

class rpc.HeaderFraming

Frames each message with a header block giving its length:

Content-Length: 42\r\n
\r\n
{"jsonrpc":"2.0","id":1,"method":"status"}

The header block is ASCII: lines of Name: value, each ended by \r\n, and a blank line after them. Content-Length gives the length of the message after the blank line, in bytes, and is required. Content-Type may be given, and if it names a charset that charset must be UTF-8. Header names are read without regard to case, and any other header is ignored. A message is sent with a Content-Length header alone.

import rpc

var framing = rpc.HeaderFraming()
var stream = framing.frame('{"a":1}') + framing.frame('{"b":2}')

# However the bytes are split, the same two messages come out.
var first = framing.feed(stream[0, 30])
var second = framing.feed(stream[30, stream.length()])

echo first.map(@(m) => m.to_string())   # [{"a":1}]
echo second.map(@(m) => m.to_string())  # [{"b":2}]
  • printable — has a @to_string(), so echo and print() show something useful

Constructor

rpc.HeaderFraming()

Returns a new HeaderFraming with nothing buffered and a maximum message size of DEFAULT_MAX_SIZE.

HeaderFraming.set_max_size()

rpc.HeaderFraming.set_max_size(size: int) -> HeaderFraming

Sets the largest message, in bytes, that feed() accepts.

Parameters

  • size (int) — Default DEFAULT_MAX_SIZE, 64 MiB.

Returns HeaderFraming — itself.

HeaderFraming.max_size()

rpc.HeaderFraming.max_size() -> int

The largest message, in bytes, that feed() accepts.

Returns int

HeaderFraming.pending()

rpc.HeaderFraming.pending() -> int

How many bytes are held waiting for the rest of a message.

Returns int

HeaderFraming.feed()

rpc.HeaderFraming.feed(data: bytes) -> list[bytes]

Takes the next bytes of the stream and returns every message they complete, as the bytes of its text, in the order they arrived. A message cut off at the end of data is held until the rest of it is fed.

Parameters

  • data (bytes) — The next bytes of the stream.

Returns list[bytes]

Raises RpcFramingError when the header block is not ASCII, has no Content-Length, or runs past 8 KiB, its length is not a whole number of bytes, a header line has no :, the charset is not UTF-8, or the message is larger than max_size(). Nothing after it in the stream can be read.

HeaderFraming.frame()

rpc.HeaderFraming.frame(message) -> bytes

The bytes that send message: its text with a Content-Length header in front.

Parameters

  • message (string|bytes) — The message’s text.

Returns bytes

HeaderFraming.to_string()

rpc.HeaderFraming.to_string() -> string

This framing as HeaderFraming(n bytes pending).

Returns string

LineFraming

class rpc.LineFraming

Frames each message as one line, ended by \n, the framing of newline-delimited JSON. JSON text holds no line breaks of its own, so a line is always exactly one message.

A line may end in \r\n as well, and an empty line is skipped.

import rpc

var framing = rpc.LineFraming()
var lines = framing.feed('{"a":1}\n{"b":'.to_bytes())

echo lines.length()  # 1
echo framing.feed('2}\n'.to_bytes())[0].to_string()  # {"b":2}
  • printable — has a @to_string(), so echo and print() show something useful

Constructor

rpc.LineFraming()

Returns a new LineFraming with nothing buffered and a maximum message size of DEFAULT_MAX_SIZE.

LineFraming.set_max_size()

rpc.LineFraming.set_max_size(size: int) -> LineFraming

Sets the longest line, in bytes, that feed() accepts.

Parameters

  • size (int) — Default DEFAULT_MAX_SIZE, 64 MiB.

Returns LineFraming — itself.

LineFraming.max_size()

rpc.LineFraming.max_size() -> int

The longest line, in bytes, that feed() accepts.

Returns int

LineFraming.pending()

rpc.LineFraming.pending() -> int

How many bytes are held waiting for the end of a line.

Returns int

LineFraming.feed()

rpc.LineFraming.feed(data: bytes) -> list[bytes]

Takes the next bytes of the stream and returns every line they complete, without its line break, as bytes, in the order they arrived. A line cut off at the end of data is held until the rest of it is fed.

Parameters

  • data (bytes) — The next bytes of the stream.

Returns list[bytes]

Raises RpcFramingError when a line runs past max_size(). Nothing after it in the stream can be read.

LineFraming.frame()

rpc.LineFraming.frame(message) -> bytes

The bytes that send message: its text and a \n.

Parameters

  • message (string|bytes) — The message’s text, which must hold no line break.

Returns bytes

LineFraming.to_string()

rpc.LineFraming.to_string() -> string

This framing as LineFraming(n bytes pending).

Returns string

MessageFraming

class rpc.MessageFraming

Takes each piece it is fed as one whole message, for a transport that keeps messages apart itself, such as a WebSocket, whose every read returns exactly one message. Framing a message adds nothing to it.

import rpc

var framing = rpc.MessageFraming()

echo framing.feed('{"a":1}'.to_bytes())[0].to_string()  # {"a":1}
echo framing.frame('{"b":2}').to_string()               # {"b":2}
  • printable — has a @to_string(), so echo and print() show something useful

Constructor

rpc.MessageFraming()

Returns a new MessageFraming with a maximum message size of DEFAULT_MAX_SIZE.

MessageFraming.set_max_size()

rpc.MessageFraming.set_max_size(size: int) -> MessageFraming

Sets the largest message, in bytes, that feed() accepts.

Parameters

  • size (int) — Default DEFAULT_MAX_SIZE, 64 MiB.

Returns MessageFraming — itself.

MessageFraming.max_size()

rpc.MessageFraming.max_size() -> int

The largest message, in bytes, that feed() accepts.

Returns int

MessageFraming.pending()

rpc.MessageFraming.pending() -> int

How many bytes are held waiting for the rest of a message: always 0, since every piece is a message of its own.

Returns int

MessageFraming.feed()

rpc.MessageFraming.feed(data: bytes) -> list[bytes]

Takes one whole message and returns it, alone in a list. Empty data holds no message and returns an empty list.

Parameters

  • data (bytes) — One whole message.

Returns list[bytes]

Raises RpcFramingError when the message is larger than max_size().

MessageFraming.frame()

rpc.MessageFraming.frame(message) -> bytes

The bytes that send message: its text, as it is.

Parameters

  • message (string|bytes) — The message’s text.

Returns bytes

MessageFraming.to_string()

rpc.MessageFraming.to_string() -> string

This framing as MessageFraming().

Returns string


2026, Richard Ore and Zuri contributors