[ 
https://issues.apache.org/jira/browse/THRIFT-6160?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Jens Geyer reassigned THRIFT-6160:
----------------------------------

    Assignee: Jens Geyer

> Haxe framed transport cannot read consecutive frames without an intervening 
> flush
> ---------------------------------------------------------------------------------
>
>                 Key: THRIFT-6160
>                 URL: https://issues.apache.org/jira/browse/THRIFT-6160
>             Project: Thrift
>          Issue Type: Bug
>          Components: Haxe - Library
>            Reporter: Jens Geyer
>            Assignee: Jens Geyer
>            Priority: Major
>          Time Spent: 20m
>  Remaining Estimate: 0h
>
> h3. Problem
> A Haxe {{TFramedTransport}} cannot read a second frame on the same connection 
> unless
> something flushed in between. The second frame fails on its four-byte header 
> with
> {{MESSAGE_SIZE_LIMIT}}, having consumed a few kilobytes of a 100 MB allowance.
> h3. Mechanism
> * {{readFrame()}} binds the read budget to each frame with 
> {{UpdateKnownMessageSize()}} and never returns it to the configured maximum 
> first.
> * {{TSocket}} charges every read against that budget 
> ({{CountConsumedMessageBytes}}).
> * {{ResetConsumedMessageSize()}} refuses to grow a budget an earlier, smaller 
> frame narrowed.
> Reading a frame therefore leaves the budget at zero, and the next 
> {{readFrameSize()}}
> throws immediately.
> h3. Why request/response hides it
> The only resets on the socket path are in {{flush()}} and on connect. A 
> normal exchange
> flushes when the reply is written, so the budget is restored before the next 
> request is
> read. A run of *one-way* calls does not: the generated processor returns 
> before the
> {{flush()}} for a one-way function, so a server handling two one-way calls in 
> a row fails
> the second one.
> h3. Reproduction
> Two frames of 16 and 4096 bytes back to back, default configuration:
> {noformat}
> frame 1: read 16 bytes OK
> frame 2: FAILED type=6 : CountConsumedMessageBytes(4): message size exceeds 
> limit 104857600
> {noformat}
> h3. Fix
> Reset the budget at the top of {{readFrame()}}, which is what the Delphi 
> binding does at the
> same point in the message and for the same reason (its endpoint charges reads 
> too). Reaching
> the budget from a layered transport needs a public entry point, so 
> {{TTransport}} gains
> {{ResetMessageSizeAndConsumedBytes()}}, named after the Delphi and netstd 
> equivalents. It is a
> plain method beside the existing {{UpdateKnownMessageSize()}} and 
> {{CheckReadBytesAvailable()}}
> rather than an abstract one, so no code outside the tree has to implement it.
> Three tests are added in {{lib/haxe/test/src/tests/FramedTransportTest.hx}} 
> and wired into the
> suite. The third declares a 64 MB field inside a 64-byte frame and passes 
> both before and after
> the change; it is there to show the budget has not been loosened while the 
> reset was widened.
> h3. Affected versions
> Present since 2dcefadba (THRIFT-5370), i.e. 0.15.0 onward.
> h3. Notes
> {{ResetConsumedMessageSize}} recognised only an omitted argument as "back to 
> the configured
> maximum", where netstd, Delphi, Java and C++ all use a negative one; a 
> negative therefore fell
> through to the shrink path and left the budget at -1, refusing every later 
> read. It has no caller
> in the tree, so the exposure is callers outside it and anyone porting the 
> idiom. Folded in here
> rather than tracked separately: it is two tokens in the same guard this fix 
> already depends on,
> and it comes with its own test.
> One inconsistency found alongside is *not* addressed here: 
> {{TStreamTransport.read}} does not
> charge reads against the budget, while {{TSocket.read}} does, so the two 
> endpoints disagree on
> accounting. That one needs a decision about which behaviour is correct.
> _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