Skip to main content

Module chunk

Module chunk 

Source
Expand description

Chunked calls: messages larger than one frame (issue #294).

A frame is capped (DEFAULT_FRAME_LIMIT, 16 MiB) so one peer can never make the other buffer an unbounded line. That cap stays. A message above it travels as several frames, and the host assembles it under a second, explicit bound (HostOptions::max_message). Chunking is off by default (max_message 0, issue #323): a service opts in with a size, and a host that has not refuses rk.upload.

Request (Client::call_large): the params are serialised to JSON text and cut at character boundaries into pieces of chunk_bytes (the host advertises it in hello). Each piece is an ordinary request to the reserved method METHOD_UPLOAD ({id, seq, last, text}, and method on seq 0). The host appends the pieces in order (one upload at a time per connection; a gap, a repeat or a changed id drops it), and on last parses the assembled text and runs the real method through the normal pipeline: allowlist, handler, events, response. The response to the final piece is the response to the call. A piece is plain JSON text, so no encoding crate is needed; escaping at most doubles its wire size.

Response: a client that opts in per request ("chunked_ok": true, which call_large sets) gets a result that exceeds the frame limit as {ok: true, result: null, chunked: {id, bytes, count}} instead of response_too_large; the client then reads the pieces with the reserved method METHOD_FETCH ({id, seq} answered {seq, text}) and parses the join. The host keeps one such result per connection (a new one replaces it; closing the connection drops it). A client that does not opt in still gets the typed response_too_large.

Events keep the frame limit: an event larger than it is dropped with ServiceEvent::FrameTooLarge as before.

The reserved methods are rk.upload and rk.fetch; no service method may start with rk.. They need no allowlist entry (any app that passed hello may use them), because the call they carry is authorised on its own method id: when the first piece arrives, before a byte is buffered (an unauthorised peer is refused there with not_allowed), and again when the call is dispatched. The assembled text is checked for nesting and value count before it is parsed.

Constantsยง

CHUNK_TEXT_BYTES
Largest piece of text in one chunk frame (1 MiB; escaping at most doubles it on the wire).
DEFAULT_MAX_MESSAGE
Default for HostOptions::max_message: 0, chunking off (it was 256 MiB earlier in the unpublished 0.2.7; see issue #323). A service that takes large calls opts in.
MAX_ASSEMBLIES_PER_CONNECTION
Assemblies one connection may hold at once. A new first piece replaces the one in progress, so the bytes a connection holds are at most one assembly, itself at most max_message.
MAX_JSON_DEPTH
Deepest array/object nesting an assembled request may have (serde_json itself stops at 128).
MAX_JSON_NODES
Most values (elements, members and containers) an assembled request may contain. A parsed Value costs 16 to 32 times the text of a dense array, so the count, not only the byte length, bounds the memory a parse can take: 1 Mi values is about 32 to 64 MiB.
METHOD_FETCH
Reserved method: one piece of a result too large for a frame.
METHOD_UPLOAD
Reserved method: one piece of a request too large for a frame.
RESERVED_PREFIX
Prefix no service method may use.
SUGGESTED_MAX_MESSAGE
A reasonable opt-in value for a service that does take large calls: 64 MiB.
UPLOAD_IDLE_TIMEOUT
An assembly that has not received a piece for this long is dropped (checked on every frame the connection sends, and the whole state goes with the connection).