Skip to main content

Module transport

Module transport 

Source
Expand description

#1667: a framing pump in front of tower-lsp’s transport.

tower-lsp 0.20 reads stdin through a FramedRead, and a framed stream ends at its first decode error. So one message whose body isn’t valid JSON stopped the whole server, with exit 0 and nothing on stderr. The trigger is reachable from a well-behaved client: JSON.stringify writes a lone UTF-16 surrogate in a string as \udcff, which serde_json rejects.

pump reads the client’s frames itself and forwards to the server only bodies that parse, so a bad one can’t end the stream:

  • a body that parses is forwarded unchanged;
  • a body whose only fault is a lone surrogate escape is repaired (each lone \uD800–\uDFFF becomes \uFFFD) and forwarded. U+FFFD is one UTF-16 code unit, like the surrogate it replaces, so the client’s positions into the text stay aligned with the server’s;
  • any other body is logged and skipped. Its id can’t be recovered from unparseable JSON, so no error response is possible.

A header block without Content-Length, or a stream that ends mid-frame (a truncated body), leaves nothing to resynchronise on: the pump stops, with the reason logged, and the server sees end of input.

#2239: the pump also stops once it has forwarded exit. tower-lsp 0.20 handles a message that arrives after exit by leaving its read loop early, without the clean-up it does at end of input, so Server::serve never returned and the process never exited. Ending the server’s input after exit means it only ever sees end of input next, whatever the client sends.

Enums§

Frame
What pump does with one frame’s body.

Constants§

MAX_BODY
#1771 review: the largest body pump accepts. A Content-Length is otherwise trusted and allocated in full before the body arrives, so one corrupted digit could abort the process. A frame over this is unrecoverable framing, like a missing Content-Length. 128 MiB is far above any real message (a didOpen carries one file’s text).
MAX_HEADER_LINE
The longest header line pump reads, so a client that never sends a newline can’t grow the line without limit.

Functions§

classify
Decide what to do with one frame’s body.
is_exit
Whether body is the exit notification, after which the server reads nothing more.
pump
Read framed messages from input and write the usable ones, re-framed, to output. Returns when input ends or loses its framing, or once exit is forwarded (#2239). Dropping output then ends the server’s input.