This library implements the dqlite wire protocol faithfully, but adds a
handful of defensive guards that the upstream C server and the
go-dqlite client do not. They
protect a Python client running in potentially adversarial network
contexts. The caps are configurable (pass None to disable); the stricter
validations match the C server's intent.
Bounds on how much a single decode will allocate, so a hostile or buggy
peer cannot exhaust memory. All are optional (None disables):
DEFAULT_MAX_TOTAL_ROWS(MessageDecoder(max_total_rows=...), default 10,000,000) — rows accumulated across continuation frames for one query.DEFAULT_MAX_CONTINUATION_FRAMES(MessageDecoder(max_continuation_frames=...), default 100,000) — continuation frames for one query.RowsResponse.DEFAULT_MAX_ROWS(MessageDecoder(max_rows=...), default 1,000,000) — per-frame row cap.ReadBuffer.DEFAULT_MAX_MESSAGE_SIZE(ReadBuffer(max_message_size=...), default 64 MiB) — envelope cap on a single frame.- Per-field bounds in
dqlitewire.limits:MAX_PARAM_COUNT(32,766, SQLite'sSQLITE_MAX_VARIABLE_NUMBER),MAX_COLUMN_COUNT(2000, SQLite's defaultSQLITE_MAX_COLUMN),MAX_FILE_COUNT(100),MAX_NODE_COUNT(10,000),MAX_ADDRESS_SIZE(256), and the text/blob/file-content caps (64 MiB minus framing).
DEFAULT_MAX_TOTAL_ROWS, DEFAULT_MAX_CONTINUATION_FRAMES,
MAX_ADDRESS_SIZE and MAX_NODE_COUNT are importable from dqlitewire;
the rest from dqlitewire.limits.
These match the C server's intent more closely than go-dqlite does:
decode_row_headerrequires the full 8-byte end-of-rows marker (C definesDQLITE_RESPONSE_ROWS_DONE = 0xff…ff/_PART = 0xee…ee; go-dqlite checks only the first byte).encode_value(value, ValueType.BOOLEAN)rejects arbitrary ints (accepts onlyboolor exactly0/1).FilesResponse.encode_bodyrejects non-8-aligned file content (C'sdumpFileassertslen % 8 == 0).encode_params_tuplerejectsValueType.UNIXTIMEoutbound (the C server's tuple decoder cannot decode it).StmtResponserejects a 16-byte body whenschema=1(C's V1 response is 24 bytes).
Some frames are decodable for proxy / recorded-traffic round-trips even though fresh construction of them is rejected:
ClusterRequestwithformat=0(the V0 response shape: id + address only).ClusterRequest.decode_bodydecodes it for proxy/replay use, and a decoded V0 frame re-encodes byte-identically. Fresh construction withformat=0is rejected withEncodeError, because production senders always emit V1 (id + address + role) and this client only decodes the V1ServersResponse.