[
https://issues.apache.org/jira/browse/THRIFT-6127?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Dmytro Shteflyuk resolved THRIFT-6127.
--------------------------------------
Fix Version/s: 0.25.0
Resolution: Fixed
> Ruby sockets remain reusable after I/O timeouts
> -----------------------------------------------
>
> Key: THRIFT-6127
> URL: https://issues.apache.org/jira/browse/THRIFT-6127
> 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
> When a timed Ruby socket read or write raises
> {{Thrift::TransportException::TIMED_OUT}}, the transport leaves its
> underlying socket open.
> This is unsafe once the operation has made partial progress. A read may
> already have removed bytes from the stream before a later read times out,
> while a write timeout may occur after some request bytes have been delivered.
> The connection's message boundary or delivery state is then uncertain, but
> {{open?}} still reports true and later operations are allowed to reuse it.
> The behavior is shared by {{Thrift::Socket}} and its TLS and UNIX socket
> subclasses. It also applies to sockets returned by the Ruby server transports.
> h3. Client impact
> Applications that catch a timeout may continue using a connection whose
> stream is no longer aligned with the Thrift message the application expected.
> A later request or response can consequently fail with an unrelated decoding
> or transport error, and a timed-out write cannot be retried safely on the
> same connection because its partial-delivery state is unknown.
> Callers should receive the original timeout and reconnect before performing
> more I/O.
> h3. Reproduction
> From {{lib/rb}}, run:
> {code:ruby}
> require "thrift"
> require "socket"
> local, peer = ::Socket.pair(:UNIX, :STREAM, 0)
> transport = Thrift::Socket.new
> transport.handle = local
> transport.timeout = 0.05
> peer.write("A")
> begin
> transport.read_all(2)
> rescue Thrift::TransportException => error
> puts "#{error.class}: #{error.message}"
> puts "open=#{transport.open?}"
> ensure
> transport.close
> peer.close
> end
> {code}
> The peer supplies one byte, so {{read_all(2)}} makes partial progress before
> waiting for the missing byte. Before this change, the operation raises a
> timeout but prints:
> {noformat}
> Thrift::TransportException: Socket: Timed out reading 1 bytes from
> localhost:9090
> open=true
> {noformat}
> The equivalent write path also remains open after partially writing a request
> and then timing out.
> h3. Expected behavior
> A timed read or write must preserve the original {{TIMED_OUT}} exception and
> close the affected transport. {{open?}} should then return false, subsequent
> I/O should fail with {{NOT_OPEN}}, and the peer should observe the closed
> connection.
> Successful reads and writes, including partial operations that complete
> before the deadline, should remain unchanged.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)