rpc.framing
import rpc
Everything here is re-exported by
rpc, soimport rpcis enough and the names are called asrpc.*. Importingrpc.framingon 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(), soechoandprint()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) — DefaultDEFAULT_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(), soechoandprint()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) — DefaultDEFAULT_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(), soechoandprint()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) — DefaultDEFAULT_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