fix(relay): never forward a tunneled body that lost frames (#2822)

When the relay drops mid-request, the prompt_async body frames can be
lost. The tunnel host forwarded the request to loopback as an
empty/truncated chunked body, which the server rejects with a bare 400
(empty response body) — the mobile app's 'Failed to send message (400)'.

Host now buffers request bodies (<512KB) and forwards the complete body
only once StreamEnd arrives; larger bodies still stream live. A new
hasBody flag on the request head lets the host detect a body that
delivered zero frames and abort it as an ambiguous transport failure
(which the client already retries) instead of forwarding an empty body.
A 15s body-delivery deadline converts stalled tunnels into clean aborts.
This commit is contained in:
Muhammad Zaim
2026-08-12 16:54:23 +03:00
committed by GitHub
parent 9e43b9ae46
commit 6a2f0b8135
5 changed files with 274 additions and 31 deletions
@@ -45,6 +45,8 @@ Everything a client normally sends to the single OpenChamber origin:
The host dispatcher restricts tunneled traffic to explicit path allowlists (one for HTTP, one for WS).
Request bodies crossing the tunnel are buffered on the host and forwarded to loopback only once the client's `StreamEnd` frame arrives (bodies above ~512 KB stream live instead). A body whose frames were lost in transit therefore never reaches the loopback server as an empty/truncated chunked body — the host aborts the stream and the client sees an ambiguous transport failure it can retry, instead of the loopback server's bare `400` ("Failed to send message (400)" from the mobile app).
## Authentication model
- The tunnel is **transport only**. The OpenChamber server still authenticates every tunneled request exactly as it authenticates a direct remote client. The relay path grants reachability, not authorization.