[
https://issues.apache.org/jira/browse/THRIFT-6125?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Dmytro Shteflyuk resolved THRIFT-6125.
--------------------------------------
Fix Version/s: 0.25.0
Resolution: Fixed
> Ruby client leaves transports reusable after uncertain request sends
> --------------------------------------------------------------------
>
> Key: THRIFT-6125
> URL: https://issues.apache.org/jira/browse/THRIFT-6125
> Project: Thrift
> Issue Type: Bug
> Components: Ruby - Library
> Reporter: Dmytro Shteflyuk
> Assignee: Dmytro Shteflyuk
> Priority: Major
> Fix For: 0.25.0
>
> Time Spent: 20m
> Remaining Estimate: 0h
>
> h3. Problem
> The Ruby client closes its output transport when argument serialization
> fails, but not when writing the message envelope, finishing the message, or
> flushing the transport fails.
> Once message writing has begun, an exception does not reliably indicate
> whether zero, some, or all request bytes reached the underlying connection.
> Leaving that connection open allows a later call to reuse transport state
> whose wire outcome is unknown. A buffered transport may still contain request
> data, while an unbuffered transport may already have emitted part or all of
> the request.
> Argument construction is different: when creating or assigning the generated
> arguments fails before message writing starts, no bytes have been published
> and the connection remains safe to reuse.
> h3. Client impact
> After a write or flush error, an application may catch the exception and make
> another call with the same generated client. Because the Ruby client
> currently leaves the transport open, that later request can follow incomplete
> or retained data from the failed request. This can desynchronize the client
> and server or make it unclear which request a subsequent response belongs to.
> The behavior affects both ordinary calls and oneway calls. For ordinary
> calls, a failed send must also not add a sequence ID to the pending-reply
> queue.
> h3. Reproduction
> The following example uses a transport that accepts request bytes but fails
> during flush:
> {code:ruby}
> require "thrift"
> class FailingFlushTransport < Thrift::BaseTransport
> attr_reader :closed
> def write(_data)
> end
> def flush
> raise Thrift::TransportException.new(
> Thrift::TransportException::UNKNOWN,
> "flush failed"
> )
> end
> def close
> @closed = true
> end
> end
> class EmptyArgs
> def write(_protocol)
> end
> end
> class ExampleClient
> include Thrift::Client
> end
> transport = FailingFlushTransport.new
> protocol = Thrift::BinaryProtocol.new(transport)
> client = ExampleClient.new(protocol)
> begin
> client.send_message("example", EmptyArgs)
> rescue Thrift::TransportException => error
> puts "#{error.message}; closed=#{transport.closed.inspect}"
> end
> {code}
> The current implementation prints:
> {noformat}
> flush failed; closed=nil
> {noformat}
> The transport therefore remains available for another request even though
> delivery of the failed request is uncertain.
> h3. Expected behavior
> The client should construct and assign arguments before beginning message
> output. Failures at that stage should leave the transport open and should not
> consume a sequence ID.
> After message output begins, an exception from the envelope, argument
> serialization, message end, or flush should close the output transport and
> re-raise the original exception. A failure while closing must not replace the
> original send error. Ordinary calls should enter the pending-reply queue only
> after a successful flush, and oneway calls should follow the same
> transport-closing rule.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)