Skip to content

THRIFT-6161: Charge reads against the message budget in the Haxe TStreamTransport - #3757

Merged
Jens-G merged 1 commit into
apache:masterfrom
Jens-G:THRIFT-6161
Aug 29, 2026
Merged

THRIFT-6161: Charge reads against the message budget in the Haxe TStreamTransport#3757
Jens-G merged 1 commit into
apache:masterfrom
Jens-G:THRIFT-6161

Conversation

@Jens-G

@Jens-G Jens-G commented Aug 28, 2026

Copy link
Copy Markdown
Member

THRIFT-6161 — follows THRIFT-6160 (#3755), same subsystem, independent change.

TStreamTransport does not charge reads against the message budget, so MaxMessageSize has no effect on a stream-backed connection however much a message reads, as long as no single read exceeds the limit on its own. Its flush() does not restore the allowance either.

Why this is a defect in Haxe specifically

There is no single convention across the bindings — there are three, and every other binding keeps to one:

Binding Sockets / streams charge? Boundary-aware endpoints (HTTP, pipe, whole-message buffer)
cpp, c_glib no no — only the zlib transport charges
java, netstd no yes
delphi yes yes
haxe TSocket yes, TStreamTransport no yes

Haxe is the only binding that departs from its own convention. Its four endpoint transports are TSocket (charges, 3 sites), THttpClient (charges), TFullDuplexHttpClient (charges) and TStreamTransport (charges nothing).

doc/specs/thrift-tconfiguration.md describes MaxMessageSize as "a general device to be used with any transport or protocol", expressed as the bytes remaining to be read — which only means something if reads draw it down.

Fix

Two coordinated changes, mirroring TSocket:

  • read() charges what it read
  • flush() resets the allowance, at the same point TSocket does

Both are needed. Charging without resetting would leave the budget only ever shrinking, so a long-lived connection would run itself out of it — which is exactly THRIFT-6160, fixed just before this.

Tests

Two, in StreamTest, failing before and passing after. The first reads past the limit 32 bytes at a time, so nothing but cumulative accounting can catch it; the second checks the allowance comes back.

Verified on neko — full suite, including the existing stream, constants and recursion-limit tests, all of which read through TStreamTransport and now have charged reads. The python and php targets cross-compile clean.

🤖 Generated with Claude Code

…eamTransport

Every endpoint transport in the binding charges reads against MaxMessageSize --
TSocket, THttpClient, TFullDuplexHttpClient -- except TStreamTransport, which
charged none. MaxMessageSize is specified as "a general device to be used with
any transport or protocol" and is expressed as the bytes remaining to be read
(doc/specs/thrift-tconfiguration.md), so on a stream-backed connection the limit
had no effect at all: a message could read any amount as long as no single read
exceeded it on its own.

read() now charges what it read. flush() resets the allowance, at the same point
TSocket does -- one without the other would leave the budget only ever
shrinking, and a long-lived connection would eventually run itself out of it.

There is no single convention across the bindings to be inconsistent with, but
Haxe is the only one that departs from its own: cpp and c_glib charge nowhere but
zlib, java and netstd charge only where the message boundary is known, delphi
charges everywhere, and Haxe charges everywhere except this one endpoint.

Two tests. The first reads past the limit 32 bytes at a time, so nothing but
cumulative accounting can catch it; the second checks the allowance comes back.

Verified on neko (full suite, including the existing stream, constants and
recursion-limit tests, all of which read through TStreamTransport); the python
and php targets cross-compile clean.

Client: haxe

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mergeable mergeable Bot added the haxe label Aug 28, 2026
@Jens-G
Jens-G merged commit 315a7fd into apache:master Aug 29, 2026
94 of 95 checks passed
@Jens-G
Jens-G deleted the THRIFT-6161 branch August 31, 2026 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant