You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@thrift.apache.org by "Jens Geyer (Jira)" <ji...@apache.org> on 2026/09/16 19:58:00 UTC
[jira] [Updated] (THRIFT-6230) Port the WebSocket frame reading fixes of THRIFT-6178, THRIFT-6179 and THRIFT-6180 to the D library
[ https://issues.apache.org/jira/browse/THRIFT-6230?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Jens Geyer updated THRIFT-6230:
-------------------------------
Fix Version/s: (was: 0.25.0)
> Port the WebSocket frame reading fixes of THRIFT-6178, THRIFT-6179 and THRIFT-6180 to the D library
> ---------------------------------------------------------------------------------------------------
>
> Key: THRIFT-6230
> URL: https://issues.apache.org/jira/browse/THRIFT-6230
> Project: Thrift
> Issue Type: Bug
> Components: D - Library
> Reporter: Jens Geyer
> Priority: Major
>
> {{lib/d/src/thrift/transport/websocket.d}} ({{TServerWebSocketTransport}}) still reads frames the way the C++ {{TWebSocketServer}} did before THRIFT-6178, THRIFT-6179 and THRIFT-6180. The two transports share their design, and the D one has all three defects on master. Since commit 79dc86d55 the module is built, tested and installed by {{lib/d/Makefile.am}}.
> h3. What happens
> * *A payload that does not arrive in one read is lost* (as in THRIFT-6178). {{readFrame()}} reads the payload with a single {{transport_.read()}} and takes anything short of the declared length for the end of the stream. {{TSocket.read()}} returns what one {{receive()}} produced, so a frame that spans more than one segment ends the connection. A 4,000-byte frame served 1,000 bytes per read: {{readFrame()}} returns {{false}}.
> * *Every Ping costs a stack frame* (as in THRIFT-6179). A Ping is answered with {{pong(); return readFrame();}}. 2,000 one-byte Pings, 14 kB on the wire, take the reader 256,368 bytes down the stack, and nothing limits how many Pings a peer sends.
> * *Empty and control frames are mis-framed* (as in THRIFT-6180).
> ** The masking key of a masked frame with no payload is not read, so the next header is parsed out of it: an empty Ping followed by a data frame ends in {{TTransportException: Reserved bits must be zeroes}}.
> ** {{writeFrameHeader()}} takes its length from the write buffer, which describes the frame only when {{flush()}} calls it. A Pong for an 8-byte Ping is written as {{8A 00}} followed by the eight bytes, and a Close as {{88 00 03 F1}}.
> h3. Change
> The three C++ changes, ported:
> * read the payload with {{readAll()}}; a peer that leaves part-way through a frame ({{END_OF_FILE}}) is still reported as the end of the stream,
> * loop over frames instead of calling {{readFrame()}} again after a Ping,
> * read the masking key of every masked frame, reset the read buffer for a frame with no payload, and pass {{writeFrameHeader()}} the length it describes ({{8A 08 ...}} and {{88 02 03 F1}} afterwards).
> Seven unittests in {{websocket.d}}: six fail before the change, the seventh guards {{flush()}}.
> _Drafted with AI assistance (Claude Opus 5); reviewed and filed by Jens Geyer._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)