[ 
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)

Reply via email to