Expand description
The reply document, assembled from a dispatched result and a request id.
§Why this is not a codec
Codec lifts and lowers a
ToolResult and nothing else, so nothing
in the crate yet turns a Dispatcher result plus a request id into the
document a peer reads. Rather than reach past it, this module writes that
document and says so where a reader will find it. A request-side codec
landing later replaces these four functions and nothing else, because the
serve loop knows no method names and no revision: it hands a result and an
id to this module and writes the bytes it gets back.
§Nothing is spliced, and that is the point
The obvious implementation is string concatenation — take
JsonObject::as_str and write it between two members. It is faster and
it is wrong, and the reason is worth stating because the mistake is
invisible in review and on the happy path.
JsonObject::new validates its text and then stores it verbatim. JSON
permits whitespace between tokens, so JsonObject::new("{\n\"a\":1\n}")
succeeds and as_str hands back two raw newlines. Splicing that into a
newline-delimited frame splits one reply into two lines, and a peer reading
them sees two malformed frames instead of an answer. No test of the handler
would notice, because the corruption happens afterwards, in the transport.
So every document here is built as a serde_json::Value and serialised
once. Value’s writer escapes control characters rather than passing them
through, which makes a raw newline in a reply structurally impossible
instead of merely unlikely — and it is one code path, so there is no second
serialiser to drift from the first.
§The id is re-escaped, not debug-printed
WireId::to_json is the right answer for a caller splicing an id, and
format!("{id:?}") is the wrong one in a way this crate has already paid
for: Debug for a string is escape_debug, which writes DEL as \u{7f}.
That is Rust syntax and not JSON, so the peer would see a parse error rather
than its own id. Both builders below hand the id to serde_json as a
Value, which escapes it the same way to_json does, for the same reason.
§What a parse failure is answered with
A frame that is not JSON has no id — there was never one to read — so
unidentified_error_reply writes "id": null, which is what the
specification requires and the only honest answer available. A caller that
invents an id for a frame it could not parse is telling the peer something
about a message the peer never successfully sent.
Enums§
- Reply
Error - Why a reply could not be assembled.
Functions§
- error_
reply - A failed reply to the request with this id.
- error_
reply_ with_ data - A failed reply carrying the structured
datasome codes require. - result_
reply - A successful reply to the request with this id.
- unidentified_
error_ reply - A failed reply to a frame that never had an id to answer.