Dmytro Shteflyuk created THRIFT-6144:
----------------------------------------

             Summary: Ruby BaseTransport read_all should report EOF when reads 
make no progress
                 Key: THRIFT-6144
                 URL: https://issues.apache.org/jira/browse/THRIFT-6144
             Project: Thrift
          Issue Type: Bug
          Components: Ruby - Library
            Reporter: Dmytro Shteflyuk
            Assignee: Dmytro Shteflyuk


h3. Problem

{{BaseTransport#read_all}} repeatedly calls {{read}} until it has received the 
requested number of bytes. It currently assumes that every call returns a 
non-empty string.

That assumption does not hold for every Ruby transport. {{IOStreamTransport}} 
delegates to Ruby {{IO#read}}, which returns {{nil}} at end of file, while 
layered transports can return an empty string when no bytes remain. An empty 
result makes {{read_all}} repeat forever without making progress. A {{nil}} 
result instead escapes as a raw {{NoMethodError}}.

h3. Client impact

A client reading a truncated or exhausted input through an affected transport 
can hang indefinitely when the transport returns an empty string. If the 
transport returns {{nil}}, the client receives an implementation-level Ruby 
exception rather than a typed Thrift transport exception.

Normal partial reads are also affected when the input ends after some, but not 
all, of the requested bytes have been received.

h3. Reproduction

Testing on master commit {{c2def39207a73394420088da9b4b105571dd9036}} produces:

{code:bash}
bundle exec ruby -Ilib -e '
require "timeout"
require "thrift"

[["empty string", ""], ["nil", nil]].each do |label, result|
  transport = Thrift::BaseTransport.new
  transport.define_singleton_method(:read) { |_| result }

  begin
    Timeout.timeout(0.1) { transport.read_all(1) }
  rescue => error
    puts "#{label}: #{error.class}: #{error.message}"
  end
end
'
{code}

{code}
empty string: Timeout::Error: execution expired
nil: NoMethodError: undefined method 'encoding' for nil
{code}

h3. Expected behavior

{{read_all(size)}} should continue across partial reads while each read returns 
at least one byte. If a positive-size read returns {{nil}} or an empty string 
before the requested size is satisfied, it should stop immediately and raise 
{{TransportException::END_OF_FILE}}.

A zero-size read should continue to return an empty binary string without 
invoking the underlying transport.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to