Skip to content

Commit 9d63c5b

Browse files
kpumukcodex
andcommitted
THRIFT-6129: Stream Ruby HeaderTransport ZLIB output
Client: rb Co-Authored-By: OpenAI Codex (GPT-5.6) <codex@openai.com>
1 parent 3b110f4 commit 9d63c5b

2 files changed

Lines changed: 33 additions & 17 deletions

File tree

lib/rb/lib/thrift/transport/header_transport.rb

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,6 @@ class HeaderTransport < BaseTransport
7777
# Default decompressed-size cap for ZLIB transform (~15.6 MB, matches other Thrift bindings)
7878
DEFAULT_MAX_DECOMPRESSED_SIZE = 16_384_000
7979

80-
# Chunk size for streaming inflate loop
81-
ZLIB_INFLATE_CHUNK_SIZE = 16_384
82-
8380
# Binary protocol version mask and version 1
8481
BINARY_VERSION_MASK = 0xffff0000
8582
BINARY_VERSION_1 = 0x80010000
@@ -408,25 +405,19 @@ def parse_header_format(buf)
408405
def bounded_inflate(compressed)
409406
inflater = Zlib::Inflate.new
410407
buffer = Bytes.empty_byte_buffer
411-
offset = 0
412-
begin
413-
while offset < compressed.bytesize
414-
buffer << inflater.inflate(compressed.byteslice(offset, ZLIB_INFLATE_CHUNK_SIZE))
415-
if buffer.bytesize > @max_decompressed_size
416-
raise TransportException.new(
417-
TransportException::SIZE_LIMIT,
418-
"Decompressed size exceeds limit of #{@max_decompressed_size}"
419-
)
420-
end
421-
offset += ZLIB_INFLATE_CHUNK_SIZE
422-
end
423-
buffer << inflater.finish
424-
if buffer.bytesize > @max_decompressed_size
408+
append_chunk = lambda do |chunk|
409+
if buffer.bytesize + chunk.bytesize > @max_decompressed_size
425410
raise TransportException.new(
426411
TransportException::SIZE_LIMIT,
427412
"Decompressed size exceeds limit of #{@max_decompressed_size}"
428413
)
429414
end
415+
416+
buffer << chunk
417+
end
418+
begin
419+
inflater.inflate(compressed, &append_chunk)
420+
inflater.finish(&append_chunk)
430421
buffer
431422
ensure
432423
inflater.close rescue nil

lib/rb/spec/header_transport_spec.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,31 @@
201201
expect(e.message).to match(/limit/)
202202
end
203203
end
204+
205+
it "should stream oversized ZLIB output before enforcing the limit" do
206+
write_buf = Thrift::MemoryBufferTransport.new
207+
writer = Thrift::HeaderTransport.new(write_buf)
208+
writer.add_transform(Thrift::HeaderTransformID::ZLIB)
209+
writer.write("A" * 8_000_000)
210+
writer.flush
211+
212+
written_data = write_buf.read(write_buf.available)
213+
read_trans = Thrift::HeaderTransport.new(Thrift::MemoryBufferTransport.new(written_data))
214+
read_trans.set_max_decompressed_size(100)
215+
216+
expect(Zlib::Inflate).to receive(:new).and_wrap_original do |new, *args|
217+
inflater = new.call(*args)
218+
expect(inflater).to receive(:inflate).and_wrap_original do |inflate, compressed, &block|
219+
expect(block).not_to be_nil
220+
inflate.call(compressed, &block)
221+
end
222+
inflater
223+
end
224+
225+
expect { read_trans.read(1) }.to raise_error(Thrift::TransportException) do |e|
226+
expect(e.type).to eq(Thrift::TransportException::SIZE_LIMIT)
227+
end
228+
end
204229
end
205230

206231
describe "read and frame detection" do

0 commit comments

Comments
 (0)